diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..15bdab2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml new file mode 100644 index 0000000..f020fb5 --- /dev/null +++ b/.idea/dataSources.local.xml @@ -0,0 +1,23 @@ + + + + + + #@ + ` + + + admin + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..d3281c4 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + mariadb + true + org.mariadb.jdbc.Driver + jdbc:mariadb://seonggil-maria.cmigfdh4psla.ap-northeast-2.rds.amazonaws.com:3306 + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..26b3516 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5d67138 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..257cd97 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,779 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "keyToString": { + "RequestMappingsPanelOrder0": "0", + "RequestMappingsPanelOrder1": "1", + "RequestMappingsPanelOrder2": "2", + "RequestMappingsPanelWidth0": "75", + "RequestMappingsPanelWidth1": "75", + "RequestMappingsPanelWidth2": "75", + "WebServerToolWindowFactoryState": "false", + "last_opened_file_path": "C:/SpringBootWorks/SubMarket/DiscoveryService", + "node.js.detected.package.eslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "project.structure.last.edited": "Modules", + "project.structure.proportion": "0.15", + "project.structure.side.proportion": "0.27471265", + "settings.editor.selected.configurable": "bigdataide_conn_settings", + "spring.configuration.checksum": "b9a7970c2902b48ff1e793a804d5e365" + }, + "keyToStringList": { + "DatabaseDriversLRU": [ + "mariadb" + ] + } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1653191488907 + + + 1654327661985 + + + 1654328860378 + + + 1654329645233 + + + 1654406099051 + + + 1654406716386 + + + 1654407264169 + + + 1654407495937 + + + 1654407892732 + + + 1654407919345 + + + 1654407940264 + + + 1654407969978 + + + 1654419251951 + + + 1654419270968 + + + 1654419297889 + + + 1654419642502 + + + 1654420481024 + + + 1654435081615 + + + 1654442576394 + + + 1655028818146 + + + 1655032075359 + + + 1655032484330 + + + 1655090114790 + + + 1655097579665 + + + 1655102146453 + + + 1655105770518 + + + 1655170275465 + + + 1655171853916 + + + 1655173395212 + + + 1655178318823 + + + 1655264755697 + + + 1655265177999 + + + 1655269714687 + + + 1655271323605 + + + 1655271396740 + + + 1655426133353 + + + 1663497945730 + + + 1663498719231 + + + 1663498735217 + + + 1663498897013 + + + 1663671416922 + + + 1663672884586 + + + 1663673932945 + + + 1663674805292 + + + 1663865701638 + + + 1663930457773 + + + 1663955148024 + + + 1664102235319 + + + 1664102533681 + + + 1664103094950 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ApiGateway/pom.xml b/ApiGateway/pom.xml index 1705571..713f019 100644 --- a/ApiGateway/pom.xml +++ b/ApiGateway/pom.xml @@ -10,7 +10,7 @@ com.submarket apigateway - 1.0.0 + 2.0.0 ApiGateway ApiGateway @@ -83,6 +83,24 @@ org.springframework.cloud spring-cloud-starter-bus-amqp + + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-starter-zipkin + 2.2.3.RELEASE + + diff --git a/ConfigService/pom.xml b/ConfigService/pom.xml index 240f9b7..dc8ec55 100644 --- a/ConfigService/pom.xml +++ b/ConfigService/pom.xml @@ -10,7 +10,7 @@ com.submarket configservice - 1.0.0 + 2.0.0 ConfigService ConfigService diff --git a/DiscoveryService/pom.xml b/DiscoveryService/pom.xml index 6e27966..72e0a6f 100644 --- a/DiscoveryService/pom.xml +++ b/DiscoveryService/pom.xml @@ -10,7 +10,7 @@ com.submarket discoveryservice - 1.0.0 + 2.0.0 DiscoveryService DiscoveryService @@ -33,6 +33,17 @@ spring-boot-starter-test test + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-starter-zipkin + 2.2.3.RELEASE + diff --git a/ItemService/pom.xml b/ItemService/pom.xml index 7e582be..b26e070 100644 --- a/ItemService/pom.xml +++ b/ItemService/pom.xml @@ -10,7 +10,7 @@ com.submarket itemservice - 1.0.0 + 2.0.0 ItemService ItemService @@ -18,10 +18,6 @@ 2021.0.3-SNAPSHOT - - org.springframework.boot - spring-boot-starter-data-jpa - org.springframework.boot spring-boot-starter-web @@ -66,16 +62,18 @@ 1.5.0.RC1 - + + + org.springframework.boot + spring-boot-starter-data-jpa + com.fasterxml.jackson.core jackson-databind - 2.13.1 - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml + com.fasterxml.jackson.dataformat + jackson-dataformat-xml @@ -143,6 +141,50 @@ org.springframework.kafka spring-kafka + + + + org.hibernate + hibernate-ehcache + 5.6.11.Final + + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-starter-zipkin + 2.2.3.RELEASE + + + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springdoc + springdoc-openapi-ui + 1.6.6 + + diff --git a/ItemService/src/main/java/com/submarket/itemservice/ItemServiceApplication.java b/ItemService/src/main/java/com/submarket/itemservice/ItemServiceApplication.java index 15d2546..8ea4259 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/ItemServiceApplication.java +++ b/ItemService/src/main/java/com/submarket/itemservice/ItemServiceApplication.java @@ -3,9 +3,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient +@EnableFeignClients public class ItemServiceApplication { public static void main(String[] args) { diff --git a/ItemService/src/main/java/com/submarket/itemservice/client/UserServiceClient.java b/ItemService/src/main/java/com/submarket/itemservice/client/UserServiceClient.java new file mode 100644 index 0000000..1a3749e --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/client/UserServiceClient.java @@ -0,0 +1,16 @@ +package com.submarket.itemservice.client; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient(name = "USER-SERVICE") +public interface UserServiceClient { + + // Gateway 를 통과하지 않고 내부 통신으로 이동하기 때문에 "/user-service" 가 필요하지 않음 + @GetMapping("/users/{userId}/items/{itemSeq}/liked") + ResponseEntity isLikedByUserId(@PathVariable int itemSeq, @PathVariable String userId); +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/coinfig/Resilience4JConfig.java b/ItemService/src/main/java/com/submarket/itemservice/coinfig/Resilience4JConfig.java new file mode 100644 index 0000000..5ac63fd --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/coinfig/Resilience4JConfig.java @@ -0,0 +1,32 @@ +package com.submarket.itemservice.coinfig; + +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; +import org.springframework.cloud.client.circuitbreaker.Customizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Duration; + +@Configuration +public class Resilience4JConfig { + + @Bean + public Customizer globalCustomConfiguration() { + CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() + .failureRateThreshold(4) // Open or False 100 중 4 번 실패 시 Open + .waitDurationInOpenState(Duration.ofMillis(1000)) // Open time (1s) + .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) // 수 or 시간 기반 + .slidingWindowSize(2).build(); + + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() + .timeoutDuration(Duration.ofSeconds(4)).build(); + + return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id) + .timeLimiterConfig(timeLimiterConfig) + .circuitBreakerConfig(circuitBreakerConfig).build()); + + } +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/coinfig/SwaggerConfig.java b/ItemService/src/main/java/com/submarket/itemservice/coinfig/SwaggerConfig.java new file mode 100644 index 0000000..498dffc --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/coinfig/SwaggerConfig.java @@ -0,0 +1,44 @@ +package com.submarket.itemservice.coinfig; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.customizers.OpenApiCustomiser; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public GroupedOpenApi publicApi() { + return GroupedOpenApi.builder() + .group("ItemServiceAPI") + .pathsToMatch("/**") + .addOpenApiCustomiser(buildSecurityOpenApi()) // JWT Setting Config + .build(); + } + + /** + * JWT Token Setting Config + * @return + */ + public OpenApiCustomiser buildSecurityOpenApi() { + return OpenApi -> OpenApi.addSecurityItem(new SecurityRequirement().addList("jwt token")) + .getComponents() + .addSecuritySchemes("jwt token", new SecurityScheme() + .name("Authorization") + .type(SecurityScheme.Type.HTTP) + .in(SecurityScheme.In.HEADER) + .bearerFormat("JWT") + .scheme("bearer")); + } + @Bean + public OpenAPI springShopOpenAPI() { + return new OpenAPI() + .info(new Info().title("SubMarket - ITEM-SERVICE API") + .description("SubMarket ItemService API 명세서").version("v2.0.0")); + } +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/constants/S3Constants.java b/ItemService/src/main/java/com/submarket/itemservice/constants/S3Constants.java new file mode 100644 index 0000000..fda4bf8 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/constants/S3Constants.java @@ -0,0 +1,11 @@ +package com.submarket.itemservice.constants; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class S3Constants { + public final static String IMAGE_FOLDER_PATH = "images"; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/controller/CategoryController.java b/ItemService/src/main/java/com/submarket/itemservice/controller/CategoryController.java index 64e46fe..224d5c6 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/controller/CategoryController.java +++ b/ItemService/src/main/java/com/submarket/itemservice/controller/CategoryController.java @@ -1,12 +1,18 @@ package com.submarket.itemservice.controller; import com.submarket.itemservice.dto.CategoryDto; -import com.submarket.itemservice.service.impl.CategoryService; +import com.submarket.itemservice.service.CategoryService; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; @@ -16,10 +22,17 @@ @Slf4j @RestController @RequiredArgsConstructor +@Tag(name = "Category API", description = "상품 Category 정보 API") public class CategoryController { private final CategoryService categoryService; - @GetMapping("/category") + + @Operation(summary = "모드 Category List 조회", description = "상품 CategoryList 조회", tags = {"category"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Category List 조회 성공") + }) + @GetMapping("/categories") + @Timed(value = "category.findAll", longTask = true) public ResponseEntity> findAllCategory() throws Exception { log.info(this.getClass().getName() + ">findAllCategory Start!"); Map rMap = new HashMap<>(); @@ -36,19 +49,20 @@ public ResponseEntity> findAllCategory() throws Exception { return ResponseEntity.ok().body(rMap); } - @GetMapping("/category/{categorySeq}") + + @Operation(summary = "단일 Category 조회", description = "상품 Category 상세 조회", tags = {"category"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Category List 조회 성공"), + @ApiResponse(responseCode = "404", description = "Category 번호와 일치하는 정보를 찾을 수 없음") + }) + @GetMapping("/categories/{categorySeq}") + @Timed(value = "item.find.category", longTask = true) public ResponseEntity findItemInfoByCategorySeq(@PathVariable int categorySeq) throws Exception { log.info(this.getClass().getName() + ".findItemInfoByCategorySeq Start!"); log.info("categorySeq : " + categorySeq); - CategoryDto pDto = new CategoryDto(); - pDto.setCategorySeq(categorySeq); - - CategoryDto categoryDto = categoryService.findCategory(pDto); - - log.info("categoryName : " + categoryDto.getCategoryName()); - log.info(this.getClass().getName() + ".findItemInfoByCategorySeq End!"); - return ResponseEntity.ok().body(categoryDto); + return ResponseEntity.ok().body(categoryService.findCategory( + CategoryDto.builder().categorySeq(categorySeq).build())); } } diff --git a/ItemService/src/main/java/com/submarket/itemservice/controller/ItemController.java b/ItemService/src/main/java/com/submarket/itemservice/controller/ItemController.java index 3ee4832..2983298 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/controller/ItemController.java +++ b/ItemService/src/main/java/com/submarket/itemservice/controller/ItemController.java @@ -1,20 +1,29 @@ package com.submarket.itemservice.controller; +import com.submarket.itemservice.client.UserServiceClient; import com.submarket.itemservice.dto.ItemDto; -import com.submarket.itemservice.service.impl.ItemService; -import com.submarket.itemservice.service.impl.S3Service; +import com.submarket.itemservice.service.ItemService; +import com.submarket.itemservice.service.impl.ItemServiceImpl; import com.submarket.itemservice.util.CmmUtil; import com.submarket.itemservice.util.TokenUtil; +import com.submarket.itemservice.vo.ItemInfoResponse; +import com.submarket.itemservice.vo.ItemLikedRequest; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.omg.CosNaming.NamingContextPackage.NotFound; +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; -import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,11 +31,25 @@ @RestController @Slf4j @RequiredArgsConstructor +@Tag(name = "Item API", description = "상품 정보 API") public class ItemController { private final ItemService itemService; private final TokenUtil tokenUtil; + private final UserServiceClient userServiceClient; + + private final CircuitBreakerFactory circuitBreakerFactory; + + + @Operation(summary = "상품 정보 등록", description = "상품 정보를 등록", tags = {"seller", "item"}) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "상품 정보 등록 성공"), + @ApiResponse(responseCode = "400", description = "상품 이미지 파일 변환 실패"), + @ApiResponse(responseCode = "400", description = "상품 이미지 저장 실패"), + @ApiResponse(responseCode = "500", description = "서버 Error") + }) @PostMapping(value = "/items", consumes = MediaType.ALL_VALUE) + @Timed(value = "item.save", longTask = true) public ResponseEntity saveItem(@RequestHeader HttpHeaders headers, ItemDto itemDto) throws Exception { log.info(this.getClass().getName() + ".saveItem Start!"); @@ -36,16 +59,18 @@ public ResponseEntity saveItem(@RequestHeader HttpHeaders headers, ItemD if (itemDto == null) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("상품 정보 오류"); } - int res = itemService.saveItem(itemDto); - - if (res == 0) { - ResponseEntity.status(500).body("ServerError"); - } + itemService.saveItem(itemDto); return ResponseEntity.status(HttpStatus.CREATED).body("상품 등록 완료"); } + + @Operation(summary = "모든 상품 조회", description = "모든 상품 정보 조회", tags = {"item"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "상품 정보 조회 성공") + }) @GetMapping("/items") + @Timed(value = "item.findAll", longTask = true) public ResponseEntity> findAllItem() throws Exception { log.info(this.getClass().getName() + ".findAllItem Start"); Map rMap = new HashMap<>(); @@ -58,8 +83,16 @@ public ResponseEntity> findAllItem() throws Exception { return ResponseEntity.ok().body(rMap); } + + @Operation(summary = "상품 정보 상세 조회", description = "상품 번호를 사용하여 상품 정보 상세 조회", tags = {"item"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "상품 정보 조회 성공"), + @ApiResponse(responseCode = "404", description = "상품 정보를 찾을 수 없음") + }) @GetMapping("/items/{itemSeq}") - public ResponseEntity findOneItem(@PathVariable int itemSeq) throws Exception { + @Timed(value = "item.findOne", longTask = true) + public ResponseEntity findOneItem(@RequestHeader HttpHeaders headers, + @PathVariable int itemSeq) throws Exception { log.info(this.getClass().getName() + ".findOneItem Start! (itemSeq : " + itemSeq + ")"); ItemDto pDto = new ItemDto(); @@ -68,11 +101,29 @@ public ResponseEntity findOneItem(@PathVariable int itemSeq) throws Exc // 상품 정보 가져오기 ItemDto itemDto = itemService.findItemInfo(pDto); + log.info("circuit Start"); + CircuitBreaker circuitBreaker = circuitBreakerFactory.create("userCircuit"); + + ResponseEntity circuitResult = + circuitBreaker.run(() -> userServiceClient.isLikedByUserId + (itemSeq, tokenUtil.getUserIdByToken(headers)), + throwable -> ResponseEntity.ok().body(0)); + + log.info("circuit End"); + + itemDto.setIsUserLiked(circuitResult.getBody()); + log.info(this.getClass().getName() + ".findOneItem End!"); return ResponseEntity.ok().body(itemDto); } + + @Operation(summary = "상품 비활성화", description = "상품 비활성화를 통해 노출 금지", tags = {"item", "seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "상품 정보 비활성화 성공") + }) @PostMapping("/items/{itemSeq}/off") + @Timed(value = "item.off", longTask = true) public ResponseEntity offItem(@PathVariable int itemSeq) throws Exception { ItemDto itemDto = new ItemDto(); itemDto.setItemSeq(itemSeq); @@ -81,7 +132,13 @@ public ResponseEntity offItem(@PathVariable int itemSeq) throws Exceptio return ResponseEntity.ok().body("비활성화 완료"); } + + @Operation(summary = "상품 활성화", description = "상품 활성화", tags = {"item", "seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "상품 정보 활성화 성공") + }) @PostMapping("/items/{itemSeq}/on") + @Timed(value = "item.on", longTask = true) public ResponseEntity onItem(@PathVariable int itemSeq) throws Exception { ItemDto itemDto = new ItemDto(); itemDto.setItemSeq(itemSeq); @@ -90,7 +147,14 @@ public ResponseEntity onItem(@PathVariable int itemSeq) throws Exception return ResponseEntity.ok().body("활성화 완료"); } + + @Operation(summary = "상품 정보 수정", description = "상품 정보 변경", tags = {"item", "seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "상품 정보 변경 성공"), + @ApiResponse(responseCode = "404", description = "상품 번호와 일치하는 상품 정보가 없음") + }) @PostMapping(value = "/items/modify/{itemSeq}", consumes = MediaType.ALL_VALUE) + @Timed(value = "item.modify", longTask = true) public ResponseEntity modifyItem(ItemDto itemDto, @PathVariable int itemSeq) throws Exception { log.info(this.getClass().getName() + ".modifyItem Start!"); itemDto.setItemSeq(itemSeq); @@ -100,12 +164,47 @@ public ResponseEntity modifyItem(ItemDto itemDto, @PathVariable int item return ResponseEntity.ok().body("상품 수정 완료"); } + + @Operation(summary = "상품 정보 조회 수 증가", description = "상품 조회 및 리뷰 작성 시 조회 수 증가", tags = {"item", "kafka"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "상품 조회수 증가 성공") + }) @PostMapping("/items/{itemSeq}/up") + @Timed(value = "item.count.up", longTask = true) public void upCount(@PathVariable int itemSeq, @RequestBody Map request) throws Exception { int userAge = Integer.parseInt(String.valueOf(request.get("userAge"))); log.info(this.getClass().getName() + ".upCount Start!"); - itemService.upCount(itemSeq, userAge); + itemService.upReadCount(itemSeq, userAge); log.info(this.getClass().getName() + ".upCount End!"); } + + + @GetMapping("/circuit/items/{itemSeq}") + @Timed(value = "item.findOne", longTask = true) + public ResponseEntity circuitFindOneItem(@RequestHeader HttpHeaders headers, + @PathVariable int itemSeq) throws Exception { + + ItemDto pDto = new ItemDto(); + pDto.setItemSeq(itemSeq); + + // 상품 정보 가져오기 + ItemDto result = itemService.findItemInfo(pDto); + return ResponseEntity.ok().body(ItemInfoResponse.builder() + .itemSeq(result.getItemSeq()) + .itemPrice(result.getItemPrice()) + .itemStatus(result.getItemStatus()) + .itemContents(result.getItemContents()) + .itemTitle(result.getItemTitle()) + .sellerId(result.getSellerId()) + .itemCount(result.getItemCount()) + .categorySeq(result.getCategorySeq()) + .readCount20(result.getReadCount20()) + .readCount30(result.getReadCount30()) + .readCount40(result.getReadCount40()) + .readCountOther(result.getReadCountOther()) + .mainImagePath(result.getMainImagePath()) + .subImagePath(result.getSubImagePath()).build() + ); + } } diff --git a/ItemService/src/main/java/com/submarket/itemservice/controller/ItemReviewController.java b/ItemService/src/main/java/com/submarket/itemservice/controller/ItemReviewController.java index d05add8..732d930 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/controller/ItemReviewController.java +++ b/ItemService/src/main/java/com/submarket/itemservice/controller/ItemReviewController.java @@ -1,11 +1,18 @@ package com.submarket.itemservice.controller; import com.submarket.itemservice.dto.ItemReviewDto; -import com.submarket.itemservice.service.impl.ItemReviewCheckService; -import com.submarket.itemservice.service.impl.ItemReviewService; +import com.submarket.itemservice.exception.ItemReviewException; +import com.submarket.itemservice.exception.result.ItemReviewExceptionResult; +import com.submarket.itemservice.service.ItemReviewCheckService; +import com.submarket.itemservice.service.ItemReviewService; import com.submarket.itemservice.util.CmmUtil; import com.submarket.itemservice.util.DateUtil; import com.submarket.itemservice.util.TokenUtil; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -14,102 +21,120 @@ import org.springframework.web.bind.annotation.*; import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; import java.util.Map; @RestController @Slf4j @RequiredArgsConstructor +@Tag(name = "Item Review API", description = "상품 정보 리뷰 API") public class ItemReviewController { private final ItemReviewService itemReviewService; private final TokenUtil tokenUtil; private final ItemReviewCheckService itemReviewCheckService; + + @Operation(summary = "상품 리뷰 등록", description = "상품 리뷰 등록 성공", tags = {"user", "review"}) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "상품 리뷰 생성 성공"), + @ApiResponse(responseCode = "400", description = "중복된 리뷰 정보"), + @ApiResponse(responseCode = "400", description = "리뷰 정보 유효성 검사 실패") + }) @PostMapping("/item/{itemSeq}/review") + @Timed(value = "item.review.save", longTask = true) public ResponseEntity saveReview(@RequestHeader HttpHeaders headers, @RequestBody ItemReviewDto itemReviewDto, @PathVariable int itemSeq) throws Exception { log.info(this.getClass().getName() + ".saveReview Start!"); - String userId = CmmUtil.nvl(tokenUtil.getUserIdByToken(headers)); - itemReviewDto.setUserId(userId); + final String userIdByToken = CmmUtil.nvl(tokenUtil.getUserIdByToken(headers)); itemReviewDto.setReviewDate(DateUtil.getDateTime("yyyyMMdd")); - if (itemReviewDto.equals(null)) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("리뷰 정보를 입력해주세요"); + if (itemReviewDto == null) { + throw new ItemReviewException(ItemReviewExceptionResult.ITEM_REVIEW_IS_NULL); } - if (itemReviewCheckService.canCreateReview(itemReviewDto, itemSeq)) { - int res = itemReviewService.saveReview(itemReviewDto, itemSeq); + + if (itemReviewCheckService.canCreateReview(ItemReviewDto.builder() // 리뷰 생성 로직 Check + .userId(userIdByToken) + .reviewDate(DateUtil.getDateTime("yyyyMMdd")).build(), itemSeq)) { + + itemReviewService.saveReview(itemReviewDto, itemSeq); } else { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("이미 작성한 리뷰가 있습니다"); + throw new ItemReviewException(ItemReviewExceptionResult.ITEM_REVIEW_ALREADY_CREATED); } - - log.info(this.getClass().getName() + ".saveReview End!"); + log.debug(this.getClass().getName() + ".saveReview End!"); return ResponseEntity.status(HttpStatus.CREATED).body("리뷰 작성 완료"); } + @Operation(summary = "상품 리뷰 정보 변경", description = "상품 리뷰 정보 변경", tags = {"user", "review"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "리뷰 정보 변경 성공"), + }) @PostMapping("/item/review/{reviewSeq}/modify") + @Timed(value = "item.review.modify", longTask = true) public ResponseEntity modifyItemReview(@RequestBody ItemReviewDto itemReviewDto, @PathVariable int reviewSeq) - throws Exception { - log.info(this.getClass().getName() + ".modifyItemReview Start!"); + throws Exception { + log.debug(this.getClass().getName() + ".modifyItemReview Start!"); + - itemReviewDto.setReviewSeq(reviewSeq); - int res = itemReviewService.modifyReview(itemReviewDto); + itemReviewService.modifyReview(ItemReviewDto.builder().reviewSeq(reviewSeq).build()); - log.info(this.getClass().getName() + ".modifyItemReview End!"); + log.debug(this.getClass().getName() + ".modifyItemReview End!"); return ResponseEntity.status(HttpStatus.OK).body("리뷰 변경 완료"); } + + @Operation(summary = "상품 리뷰 정보 삭제", description = "상품 리뷰 정보 삭제", tags = {"user", "review"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "리뷰 정보 삭제 성공"), + }) @PostMapping("/item/review/{reviewSeq}/delete") + @Timed(value = "item.review.delete", longTask = true) public ResponseEntity deleteItemReview(@PathVariable int reviewSeq) throws Exception { - log.info(this.getClass().getName() + ".deleteReview Start!"); - - ItemReviewDto itemReviewDto = new ItemReviewDto(); - itemReviewDto.setReviewSeq(reviewSeq); + log.debug(this.getClass().getName() + ".deleteReview Start!"); - itemReviewService.deleteReview(itemReviewDto); + itemReviewService.deleteReview(ItemReviewDto.builder().reviewSeq(reviewSeq).build()); - log.info(this.getClass().getName() + ".deleteReview End!"); + log.debug(this.getClass().getName() + ".deleteReview End!"); return ResponseEntity.status(HttpStatus.OK).body("리뷰 삭제 완료"); } + + @Operation(summary = "상품 리뷰 정보 조회", description = "상품에 달린 리뷰 정보 전체 조회", tags = {"item", "review"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "리뷰 정보 삭제 성공"), + @ApiResponse(responseCode = "404", description = "일치하는 상품 정보가 없음") + }) @GetMapping("/item/{itemSeq}/review") + @Timed(value = "item.review.findReviewInfo", longTask = true) public ResponseEntity> findItemReviewInItem(@PathVariable int itemSeq) throws Exception { - log.info(this.getClass().getName() + "findItemReviewInItem Start!"); - - Map rMap = new HashMap<>(); - - List itemReviewDtoList = itemReviewService.findAllReviewInItem(itemSeq); - - - rMap.put("response", itemReviewDtoList); - - log.info(this.getClass().getName() + "findItemReviewInItem End!"); - - return ResponseEntity.ok().body(rMap); + log.debug(this.getClass().getName() + "findItemReviewInItem Start!"); + log.debug(this.getClass().getName() + "findItemReviewInItem End!"); + + return ResponseEntity.ok().body(new HashMap(){ + { + put("response", itemReviewService.findAllReviewInItem(itemSeq)); + } + }); } + @Operation(summary = "상품 리뷰 정보 조회", description = "특정 사용자가 작성한 리뷰 목록 조회", tags = {"user", "review"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "리뷰 정보 조회 성공"), + }) @GetMapping("/item/review") + @Timed(value = "user.item.review.find", longTask = true) public ResponseEntity> findReviewByUserId(@RequestHeader HttpHeaders headers) throws Exception { - String userId = CmmUtil.nvl(tokenUtil.getUserIdByToken(headers)); - - Map rMap = new HashMap<>(); - - List itemReviewDtoList = itemReviewService.findAllReviewByUserId(userId); - - if (itemReviewDtoList == null) { - itemReviewDtoList = new LinkedList(); - } - - rMap.put("response", itemReviewDtoList); - + final String userIdByToken = CmmUtil.nvl(tokenUtil.getUserIdByToken(headers)); - return ResponseEntity.ok().body(rMap); + return ResponseEntity.ok().body(new HashMap(){ + { + put("response", itemReviewService.findAllReviewByUserId(userIdByToken)); + } + }); } } \ No newline at end of file diff --git a/ItemService/src/main/java/com/submarket/itemservice/controller/MainController.java b/ItemService/src/main/java/com/submarket/itemservice/controller/MainController.java index 5100aed..7e2cf1b 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/controller/MainController.java +++ b/ItemService/src/main/java/com/submarket/itemservice/controller/MainController.java @@ -1,22 +1,12 @@ package com.submarket.itemservice.controller; -import com.submarket.itemservice.dto.ItemDto; -import com.submarket.itemservice.jpa.CategoryRepository; -import com.submarket.itemservice.jpa.ItemRepository; -import com.submarket.itemservice.jpa.ItemReviewRepository; -import com.submarket.itemservice.service.impl.ItemService; -import com.submarket.itemservice.service.impl.S3Service; -import com.submarket.itemservice.service.impl.SchedulerService; +import com.submarket.itemservice.service.impl.SchedulerServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; @RestController @Slf4j @@ -24,12 +14,7 @@ public class MainController { private final Environment env; - private final CategoryRepository categoryRepository; - private final ItemService itemService; - private final ItemRepository itemRepository; - private final ItemReviewRepository itemReviewRepository; - private final S3Service s3Service; - private final SchedulerService schedulerService; + private final SchedulerServiceImpl schedulerServiceImpl; @GetMapping("/health") public String health() { @@ -43,7 +28,7 @@ public String health() { @GetMapping("/test") public void test() throws Exception{ - schedulerService.createSellerTotalSales(); + schedulerServiceImpl.createSellerTotalSales(); } } diff --git a/ItemService/src/main/java/com/submarket/itemservice/controller/ReadCountController.java b/ItemService/src/main/java/com/submarket/itemservice/controller/ReadCountController.java index 176fbae..d252963 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/controller/ReadCountController.java +++ b/ItemService/src/main/java/com/submarket/itemservice/controller/ReadCountController.java @@ -1,7 +1,12 @@ package com.submarket.itemservice.controller; import com.submarket.itemservice.dto.ItemDto; -import com.submarket.itemservice.service.impl.ItemService; +import com.submarket.itemservice.service.impl.ItemServiceImpl; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -13,11 +18,17 @@ @RestController @Slf4j @RequiredArgsConstructor +@Tag(name = "Item ReadCount API", description = "상품 조회 수 관련 API") public class ReadCountController { - private final ItemService itemService; + private final ItemServiceImpl itemService; + @Operation(summary = "사용자 나이별 상품 조회", description = "나이별 선호하는 상품 순으로 정렬 후 전달", tags = {"user", "item"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "조회 성공") + }) @GetMapping("/item/read/{age}") + @Timed(value = "item.item.sort.readCount", longTask = true) public ResponseEntity> findItemInfoByReadCount(@PathVariable int age) throws Exception { log.info(this.getClass().getName() + "."); Map rMap = new HashMap<>(); @@ -52,10 +63,15 @@ public int compare(ItemDto o1, ItemDto o2) { return ResponseEntity.status(HttpStatus.OK).body(rMap); } + @Operation(summary = "사용자 나이별 상품 조회 수 증가", description = "사용자 나이를 조회하여 조회 수 증가", tags = {"user", "item"}) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "조회 수 증가 성공") + }) @GetMapping("/item/{itemSeq}/countUp/{userAge}") + @Timed(value = "item.count.up.age", longTask = true) public ResponseEntity itemCountUp(@PathVariable int itemSeq, @PathVariable int userAge) throws Exception { log.info(this.getClass().getName() + ".itemCountUp Start"); - itemService.upCount(itemSeq, userAge); + itemService.upReadCount(itemSeq, userAge); return ResponseEntity.status(HttpStatus.CREATED).body("UpCount"); } diff --git a/ItemService/src/main/java/com/submarket/itemservice/controller/SellerItemController.java b/ItemService/src/main/java/com/submarket/itemservice/controller/SellerItemController.java index 34000d6..0ef4430 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/controller/SellerItemController.java +++ b/ItemService/src/main/java/com/submarket/itemservice/controller/SellerItemController.java @@ -1,8 +1,13 @@ package com.submarket.itemservice.controller; import com.submarket.itemservice.dto.ItemDto; -import com.submarket.itemservice.service.impl.ItemService; +import com.submarket.itemservice.service.impl.ItemServiceImpl; import com.submarket.itemservice.util.TokenUtil; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -10,6 +15,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; @@ -19,12 +25,19 @@ @RestController @Slf4j @RequiredArgsConstructor +@Tag(name = "Item Seller API", description = "판매자가 자신의 상품 정보를 관리하는 API") public class SellerItemController { - private final ItemService itemService; + private final ItemServiceImpl itemService; private final TokenUtil tokenUtil; + + @Operation(summary = "판매자가 판매중인 상품 목록 조회", description = "판매자가 판매중인 상품 조회", tags = {"seller", "item"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "판매자 상품 조회 성공") + }) @GetMapping("/item/seller") + @Timed(value = "seller.item.findById", longTask = true) public ResponseEntity> findItemInfoBySellerId(@RequestHeader HttpHeaders headers) throws Exception { log.info(this.getClass().getName() + "findItemInfoBySellerId Start!"); String sellerId = tokenUtil.getUserIdByToken(headers); diff --git a/ItemService/src/main/java/com/submarket/itemservice/dto/CategoryDto.java b/ItemService/src/main/java/com/submarket/itemservice/dto/CategoryDto.java index 5824e8d..cda605c 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/dto/CategoryDto.java +++ b/ItemService/src/main/java/com/submarket/itemservice/dto/CategoryDto.java @@ -1,7 +1,9 @@ package com.submarket.itemservice.dto; import com.submarket.itemservice.jpa.entity.ItemEntity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,8 +12,10 @@ @Data @AllArgsConstructor @NoArgsConstructor +@Builder public class CategoryDto { private int categorySeq; // 1, 2 + private String categoryName; // 식품, 음료 private List items; diff --git a/ItemService/src/main/java/com/submarket/itemservice/dto/ItemDto.java b/ItemService/src/main/java/com/submarket/itemservice/dto/ItemDto.java index 235fcea..5618474 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/dto/ItemDto.java +++ b/ItemService/src/main/java/com/submarket/itemservice/dto/ItemDto.java @@ -1,40 +1,59 @@ package com.submarket.itemservice.dto; import com.submarket.itemservice.jpa.entity.CategoryEntity; +import com.submarket.itemservice.jpa.entity.ItemReviewEntity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.web.multipart.MultipartFile; import javax.persistence.Column; +import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor +@Builder public class ItemDto { private int itemSeq; + private String sellerId; + private String itemTitle; + private String itemContents; private int itemPrice; + private int itemCount; // 상품 수 private int categorySeq; - private CategoryEntity category; private int itemStatus; // 활성화 private int readCount20; + private int readCount30; + private int readCount40; + private int readCountOther; private int userAge; - private String mainImagePath; // DB에 저장되어 있는 이미지 정보 + private String subImagePath; + private int isUserLiked; + + + + private CategoryEntity category; + + private List reviews; + private MultipartFile mainImage; // Front 에서 넘어온 이미지 private MultipartFile subImage; // Image 2 diff --git a/ItemService/src/main/java/com/submarket/itemservice/dto/ItemReviewDto.java b/ItemService/src/main/java/com/submarket/itemservice/dto/ItemReviewDto.java index 9190e77..d4b4832 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/dto/ItemReviewDto.java +++ b/ItemService/src/main/java/com/submarket/itemservice/dto/ItemReviewDto.java @@ -1,19 +1,28 @@ package com.submarket.itemservice.dto; import com.submarket.itemservice.jpa.entity.ItemEntity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor +@Builder public class ItemReviewDto { private Integer reviewSeq; + private String userId; + private String userAge; + private int reviewStar; + private String reviewContents; + private String reviewDate; + private ItemEntity item; } diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/CategoryException.java b/ItemService/src/main/java/com/submarket/itemservice/exception/CategoryException.java new file mode 100644 index 0000000..4cf08d6 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/CategoryException.java @@ -0,0 +1,13 @@ +package com.submarket.itemservice.exception; + +import com.submarket.itemservice.exception.result.CategoryExceptionResult; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class CategoryException extends RuntimeException { + + + private final CategoryExceptionResult exceptionResult; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/ErrorResponse.java b/ItemService/src/main/java/com/submarket/itemservice/exception/ErrorResponse.java new file mode 100644 index 0000000..bdbfa8b --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/ErrorResponse.java @@ -0,0 +1,13 @@ +package com.submarket.itemservice.exception; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +@Builder +public class ErrorResponse { + private final String code; + private final String message; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/ItemException.java b/ItemService/src/main/java/com/submarket/itemservice/exception/ItemException.java new file mode 100644 index 0000000..84f7751 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/ItemException.java @@ -0,0 +1,13 @@ +package com.submarket.itemservice.exception; + +import com.submarket.itemservice.exception.result.ItemExceptionResult; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ItemException extends RuntimeException { + + + private final ItemExceptionResult exceptionResult; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/ItemReviewException.java b/ItemService/src/main/java/com/submarket/itemservice/exception/ItemReviewException.java new file mode 100644 index 0000000..7c37063 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/ItemReviewException.java @@ -0,0 +1,13 @@ +package com.submarket.itemservice.exception; + +import com.submarket.itemservice.exception.result.ItemReviewExceptionResult; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ItemReviewException extends RuntimeException { + + + private final ItemReviewExceptionResult exceptionResult; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/ItemServiceExceptionHandler.java b/ItemService/src/main/java/com/submarket/itemservice/exception/ItemServiceExceptionHandler.java new file mode 100644 index 0000000..ea71b38 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/ItemServiceExceptionHandler.java @@ -0,0 +1,65 @@ +package com.submarket.itemservice.exception; + +import com.submarket.itemservice.exception.ErrorResponse; +import com.submarket.itemservice.exception.ItemException; +import com.submarket.itemservice.exception.ItemReviewException; +import com.submarket.itemservice.exception.result.CategoryExceptionResult; +import com.submarket.itemservice.exception.result.ItemExceptionResult; +import com.submarket.itemservice.exception.result.ItemReviewExceptionResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +@Slf4j +@RestControllerAdvice +public class ItemServiceExceptionHandler extends ResponseEntityExceptionHandler { + + + // Catch ItemException + @ExceptionHandler({ItemException.class}) + public ResponseEntity handleItemException(final ItemException exception) { + log.warn("ItemException occur : ", exception); + return this.makeErrorResponseEntity(exception.getExceptionResult()); + } + + + // Catch ItemReviewException + @ExceptionHandler(ItemReviewException.class) + public ResponseEntity handleItemReviewException(final ItemReviewException exception) { + log.warn("ItemReviewException occur : ", exception); + return this.makeErrorResponseEntity(exception.getExceptionResult()); + } + + + @ExceptionHandler(CategoryException.class) + public ResponseEntity handleCategoryException(final CategoryException exception) { + log.warn("CategoryException occur : ", exception); + return this.makeErrorResponseEntity(exception.getExceptionResult()); + + } + + + /** + * Exception 정보에서 ResponseEntity(ErrorResponse 생성) + * -> 각 Exception 에 맞에 Param 변경 + * @param exceptionResult + * @return + */ + + private ResponseEntity makeErrorResponseEntity(final CategoryExceptionResult exceptionResult) { + return ResponseEntity.status(exceptionResult.getStatus()) + .body(new ErrorResponse(exceptionResult.name(), exceptionResult.getMessage())); + } + + private ResponseEntity makeErrorResponseEntity(final ItemReviewExceptionResult exceptionResult) { + return ResponseEntity.status(exceptionResult.getStatus()) + .body(new ErrorResponse(exceptionResult.name(), exceptionResult.getMessage())); + } + + private ResponseEntity makeErrorResponseEntity(final ItemExceptionResult exceptionResult) { + return ResponseEntity.status(exceptionResult.getStatus()) + .body(new ErrorResponse(exceptionResult.name(), exceptionResult.getMessage())); + } +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/result/CategoryExceptionResult.java b/ItemService/src/main/java/com/submarket/itemservice/exception/result/CategoryExceptionResult.java new file mode 100644 index 0000000..e755768 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/result/CategoryExceptionResult.java @@ -0,0 +1,16 @@ +package com.submarket.itemservice.exception.result; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum CategoryExceptionResult { + CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "Category 정보를 찾을 수 없습니다"), + ; + + + private final HttpStatus status; + private final String message; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/result/ItemExceptionResult.java b/ItemService/src/main/java/com/submarket/itemservice/exception/result/ItemExceptionResult.java new file mode 100644 index 0000000..9e36719 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/result/ItemExceptionResult.java @@ -0,0 +1,16 @@ +package com.submarket.itemservice.exception.result; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum ItemExceptionResult { + ITEM_NOT_FOUND(HttpStatus.NOT_FOUND, "상품 정보를 찾을 수 없습니다"), + ; + + + private final HttpStatus status; + private final String message; + } diff --git a/ItemService/src/main/java/com/submarket/itemservice/exception/result/ItemReviewExceptionResult.java b/ItemService/src/main/java/com/submarket/itemservice/exception/result/ItemReviewExceptionResult.java new file mode 100644 index 0000000..4878d58 --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/exception/result/ItemReviewExceptionResult.java @@ -0,0 +1,17 @@ +package com.submarket.itemservice.exception.result; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum ItemReviewExceptionResult { + ITEM_REVIEW_IS_NULL(HttpStatus.BAD_REQUEST, "리뷰 정보를 입력해주세요"), + ITEM_REVIEW_ALREADY_CREATED(HttpStatus.BAD_REQUEST, "이미 작성된 리뷰가 있습니다.") + ; + + + private final HttpStatus status; + private final String message; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/jpa/CategoryRepository.java b/ItemService/src/main/java/com/submarket/itemservice/jpa/CategoryRepository.java index bd53d71..17a5ad4 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/jpa/CategoryRepository.java +++ b/ItemService/src/main/java/com/submarket/itemservice/jpa/CategoryRepository.java @@ -1,13 +1,13 @@ package com.submarket.itemservice.jpa; import com.submarket.itemservice.jpa.entity.CategoryEntity; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; import javax.transaction.Transactional; @Repository -@Transactional -public interface CategoryRepository extends CrudRepository { +public interface CategoryRepository extends JpaRepository { } diff --git a/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemRepository.java b/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemRepository.java index b10c375..0660c4f 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemRepository.java +++ b/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemRepository.java @@ -1,6 +1,7 @@ package com.submarket.itemservice.jpa; import com.submarket.itemservice.jpa.entity.ItemEntity; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; @@ -12,79 +13,76 @@ import java.util.Optional; @Repository -@Transactional -public interface ItemRepository extends CrudRepository { +public interface ItemRepository extends JpaRepository { @Override - @Transactional Optional findById(Integer integer); - @Transactional List findAllBySellerId(String sellerId); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET item_status = 0 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET itemStatus = 0 WHERE item_seq = :itemSeq", nativeQuery = true) int offItemStatus(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET item_status = 1 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET itemStatus = 1 WHERE item_seq = :itemSeq", nativeQuery = true) int onItemStatus(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET item_contents = :itemContents, item_price = :itemPrice, item_count = :itemCount," + - "item_title = :itemTitle WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET itemContents = :itemContents, itemPrice = :itemPrice, itemCount = :itemCount," + + "itemTitle = :itemTitle WHERE item_seq = :itemSeq", nativeQuery = true) int modifyItem(@Param("itemSeq") int itemSeq, @Param("itemContents") String itemContents, @Param("itemPrice") int itemPrice, @Param("itemCount") int itemCount, @Param("itemTitle") String itemTitle); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET item_count = item_count - 1 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET itemCount = itemCount - 1 WHERE item_seq = :itemSeq", nativeQuery = true) void reduceItemCount(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET item_count = item_count + 1 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET itemCount = itemCount + 1 WHERE item_seq = :itemSeq", nativeQuery = true) void increaseItemCount(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count20 = read_count20 + 1 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCount20 = readCount20 + 1 WHERE item_seq = :itemSeq", nativeQuery = true) void increaseReadCount20(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count30 = read_count30 + 1 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCount30 = readCount30 + 1 WHERE item_seq = :itemSeq", nativeQuery = true) void increaseReadCount30(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count40 = read_count40 + 1 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCount40 = readCount40 + 1 WHERE item_seq = :itemSeq", nativeQuery = true) void increaseReadCount40(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count_other = read_count_other + 1 WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCount_other = readCountOther + 1 WHERE item_seq = :itemSeq", nativeQuery = true) void increaseReadCountOther(@Param("itemSeq") int itemSeq); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count20 = read_count20 + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCount20 = readCount20 + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) void increaseCustomReadCount20(@Param("itemSeq") int itemSeq, @Param("readValue") int readValue); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count30 = read_count30 + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCount30 = readCount30 + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) void increaseCustomReadCount30(@Param("itemSeq") int itemSeq, @Param("readValue") int readValue); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count40 = read_count40 + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCount40 = readCount40 + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) void increaseCustomReadCount40(@Param("itemSeq") int itemSeq, @Param("readValue") int readValue); - @Modifying + @Modifying(clearAutomatically = true) @Transactional - @Query(value = "UPDATE item_info SET read_count_other = read_count_other + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) + @Query(value = "UPDATE ITEM SET readCountOther = readCountOther + :readValue WHERE item_seq = :itemSeq", nativeQuery = true) void increaseCustomReadCountOther(@Param("itemSeq") int itemSeq, @Param("readValue") int readValue); } diff --git a/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemReviewRepository.java b/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemReviewRepository.java index e10acd9..4eaf73b 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemReviewRepository.java +++ b/ItemService/src/main/java/com/submarket/itemservice/jpa/ItemReviewRepository.java @@ -2,6 +2,7 @@ import com.submarket.itemservice.jpa.entity.ItemEntity; import com.submarket.itemservice.jpa.entity.ItemReviewEntity; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; @@ -13,7 +14,7 @@ import java.util.Optional; @Repository -public interface ItemReviewRepository extends CrudRepository { +public interface ItemReviewRepository extends JpaRepository { @Override @Transactional @@ -28,8 +29,8 @@ public interface ItemReviewRepository extends CrudRepository findAllByUserId(String userId); @Transactional - @Modifying - @Query(value = "UPDATE item_review_info SET review_contents = :reviewContents, review_date = :reviewDate, review_star = :reviewStar WHERE review_seq = :reviewSeq", nativeQuery = true) + @Modifying(clearAutomatically = true) + @Query(value = "UPDATE ITEM_REVIEW SET reviewContents = :reviewContents, reviewDate = :reviewDate, reviewStar = :reviewStar WHERE reviewSeq = :reviewSeq", nativeQuery = true) void modifyItemReview(@Param("reviewContents") String reviewContents, @Param("reviewDate") String reviewDate, @Param("reviewStar") int reviewStar, @Param("reviewSeq") int reviewSeq); } diff --git a/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/CategoryEntity.java b/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/CategoryEntity.java index fae0a39..86ff6af 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/CategoryEntity.java +++ b/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/CategoryEntity.java @@ -1,9 +1,6 @@ package com.submarket.itemservice.jpa.entity; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import javax.persistence.*; import java.util.ArrayList; @@ -14,7 +11,8 @@ @Builder @AllArgsConstructor @NoArgsConstructor -@Table(name = "categoryInfo") +@Table(name = "CATEGORY") +@Cacheable public class CategoryEntity { @Id @@ -25,7 +23,7 @@ public class CategoryEntity { private String categoryName; @OneToMany(mappedBy = "category") - List items = new ArrayList<>(); + List items; diff --git a/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemEntity.java b/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemEntity.java index b3886ab..275777f 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemEntity.java +++ b/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemEntity.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; import javax.persistence.*; import java.util.List; @@ -12,9 +14,12 @@ @Getter @Builder @Entity -@Table(name = "itemInfo") -@JsonIgnoreProperties({"category", "reviews"}) +@Table(name = "ITEM") +@ToString(exclude={"category"}) @Setter +@DynamicInsert +@DynamicUpdate +@Cacheable public class ItemEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemReviewEntity.java b/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemReviewEntity.java index 8f8f2e6..1706f9a 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemReviewEntity.java +++ b/ItemService/src/main/java/com/submarket/itemservice/jpa/entity/ItemReviewEntity.java @@ -2,10 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import javax.persistence.*; @@ -14,8 +11,9 @@ @Getter @Builder @Entity -@Table(name = "itemReviewInfo") -@JsonIgnoreProperties({"item"}) +@Table(name = "ITEM_REVIEW") +@ToString(exclude={"item"}) +@Cacheable public class ItemReviewEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/ItemService/src/main/java/com/submarket/itemservice/mapper/CategoryMapper.java b/ItemService/src/main/java/com/submarket/itemservice/mapper/CategoryMapper.java index 3e735dd..372d799 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/mapper/CategoryMapper.java +++ b/ItemService/src/main/java/com/submarket/itemservice/mapper/CategoryMapper.java @@ -8,9 +8,7 @@ @Mapper public interface CategoryMapper { CategoryMapper INSTANCE = Mappers.getMapper(CategoryMapper.class); - CategoryDto categoryEntityToCategoryDto(CategoryEntity categoryEntity); - CategoryEntity categoryDtoToCategoryEntity(CategoryDto categoryDto); } diff --git a/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemMapper.java b/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemMapper.java index 7aad2d8..401fb8e 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemMapper.java +++ b/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemMapper.java @@ -5,12 +5,12 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; + @Mapper public interface ItemMapper { ItemMapper INSTANCE = Mappers.getMapper(ItemMapper.class); - ItemEntity itemDtoToItemEntity(ItemDto itemDto); - @Mapping(target = "category", ignore = true) ItemDto itemEntityToItemDto(ItemEntity itemEntity); + ItemEntity itemDtoToItemEntity(ItemDto itemDto); } diff --git a/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemReviewMapper.java b/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemReviewMapper.java index 1ac0e35..88368a5 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemReviewMapper.java +++ b/ItemService/src/main/java/com/submarket/itemservice/mapper/ItemReviewMapper.java @@ -5,6 +5,7 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; + @Mapper public interface ItemReviewMapper { ItemReviewMapper INSTANCE = Mappers.getMapper(ItemReviewMapper.class); diff --git a/ItemService/src/main/java/com/submarket/itemservice/security/WebSecurity.java b/ItemService/src/main/java/com/submarket/itemservice/security/WebSecurity.java index 8d223ab..7d633e5 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/security/WebSecurity.java +++ b/ItemService/src/main/java/com/submarket/itemservice/security/WebSecurity.java @@ -17,8 +17,8 @@ public class WebSecurity extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); - http.authorizeRequests().antMatchers("/**") - .hasIpAddress(env.getProperty("gateway.ip")); // IP + http.authorizeRequests().antMatchers("/**").permitAll(); +// .hasIpAddress("127.0.0.1"); // IP http.headers().frameOptions().disable(); } } \ No newline at end of file diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/ICategoryService.java b/ItemService/src/main/java/com/submarket/itemservice/service/CategoryService.java similarity index 87% rename from ItemService/src/main/java/com/submarket/itemservice/service/ICategoryService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/CategoryService.java index 4020e2b..4d2ef07 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/ICategoryService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/CategoryService.java @@ -4,7 +4,7 @@ import java.util.List; -public interface ICategoryService { +public interface CategoryService { CategoryDto findCategory(CategoryDto categoryDto) throws Exception; diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/IItemReviewCheckService.java b/ItemService/src/main/java/com/submarket/itemservice/service/ItemReviewCheckService.java similarity index 81% rename from ItemService/src/main/java/com/submarket/itemservice/service/IItemReviewCheckService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/ItemReviewCheckService.java index f121894..7a5d55c 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/IItemReviewCheckService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/ItemReviewCheckService.java @@ -2,6 +2,6 @@ import com.submarket.itemservice.dto.ItemReviewDto; -public interface IItemReviewCheckService { +public interface ItemReviewCheckService { boolean canCreateReview(ItemReviewDto itemReviewDto, int itemSeq) throws Exception; } diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/IItemReviewService.java b/ItemService/src/main/java/com/submarket/itemservice/service/ItemReviewService.java similarity index 52% rename from ItemService/src/main/java/com/submarket/itemservice/service/IItemReviewService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/ItemReviewService.java index de814d0..61fc6b7 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/IItemReviewService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/ItemReviewService.java @@ -4,12 +4,12 @@ import java.util.List; -public interface IItemReviewService { - int saveReview(ItemReviewDto itemReviewDto, int itemSeq) throws Exception; +public interface ItemReviewService { + void saveReview(ItemReviewDto itemReviewDto, int itemSeq) throws Exception; - int modifyReview(ItemReviewDto itemReviewDto) throws Exception; + void modifyReview(ItemReviewDto itemReviewDto) throws Exception; - int deleteReview(ItemReviewDto itemReviewDto) throws Exception; + void deleteReview(ItemReviewDto itemReviewDto) throws Exception; List findAllReviewInItem(int itemSeq) throws Exception; diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/IItemService.java b/ItemService/src/main/java/com/submarket/itemservice/service/ItemService.java similarity index 59% rename from ItemService/src/main/java/com/submarket/itemservice/service/IItemService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/ItemService.java index 1262d1d..dfb53da 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/IItemService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/ItemService.java @@ -4,8 +4,8 @@ import java.util.List; -public interface IItemService { - int saveItem(ItemDto itemDto) throws Exception; +public interface ItemService { + void saveItem(ItemDto itemDto) throws Exception; ItemDto findItemInfo(ItemDto itemDto) throws Exception; @@ -15,13 +15,13 @@ public interface IItemService { int onItem(ItemDto itemDto) throws Exception; - int modifyItem(ItemDto itemDto) throws Exception; + void modifyItem(ItemDto itemDto) throws Exception; List findItemBySellerId(String sellerId) throws Exception; - void upCount(int itemSeq, int userAge) throws Exception; + void upReadCount(int itemSeq, int userAge) throws Exception; - void upCountCustom(int itemSeq, int userAge, int readValue) throws Exception; + void upReadCount(int itemSeq, int userAge, int readValue) throws Exception; diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/IKafkaConsumerService.java b/ItemService/src/main/java/com/submarket/itemservice/service/KafkaConsumerService.java similarity index 85% rename from ItemService/src/main/java/com/submarket/itemservice/service/IKafkaConsumerService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/KafkaConsumerService.java index 09a1164..6e74d93 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/IKafkaConsumerService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/KafkaConsumerService.java @@ -1,6 +1,6 @@ package com.submarket.itemservice.service; -public interface IKafkaConsumerService { +public interface KafkaConsumerService { void reduceItemCount(String kafkaMessage) throws Exception; void increaseItemCount(String kafkaMessage) throws Exception; diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/IKafkaProducerService.java b/ItemService/src/main/java/com/submarket/itemservice/service/KafkaProducerService.java similarity index 80% rename from ItemService/src/main/java/com/submarket/itemservice/service/IKafkaProducerService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/KafkaProducerService.java index 3760c1a..8b5e9cf 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/IKafkaProducerService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/KafkaProducerService.java @@ -2,7 +2,7 @@ import com.submarket.itemservice.dto.ItemDto; -public interface IKafkaProducerService { +public interface KafkaProducerService { void sendItemInfoToOrderService(ItemDto itemDto) throws Exception; } diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/IS3Service.java b/ItemService/src/main/java/com/submarket/itemservice/service/S3Service.java similarity index 86% rename from ItemService/src/main/java/com/submarket/itemservice/service/IS3Service.java rename to ItemService/src/main/java/com/submarket/itemservice/service/S3Service.java index 52a4940..4b7ac4a 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/IS3Service.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/S3Service.java @@ -2,6 +2,6 @@ import org.springframework.web.multipart.MultipartFile; -public interface IS3Service { +public interface S3Service { String uploadImageInS3(MultipartFile multipartFile, String dirName) throws Exception; } diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/CategoryService.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/CategoryService.java deleted file mode 100644 index ecaef50..0000000 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/CategoryService.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.submarket.itemservice.service.impl; - -import com.submarket.itemservice.dto.CategoryDto; -import com.submarket.itemservice.jpa.CategoryRepository; -import com.submarket.itemservice.jpa.entity.CategoryEntity; -import com.submarket.itemservice.mapper.CategoryMapper; -import com.submarket.itemservice.service.ICategoryService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; - -@Service("CategoryService") -@Slf4j -@RequiredArgsConstructor -public class CategoryService implements ICategoryService { - - private final CategoryRepository categoryRepository; - @Override - @Transactional - public CategoryDto findCategory(CategoryDto categoryDto) throws Exception { - log.info(this.getClass().getName() + ".findCategory Start"); - int categorySeq = categoryDto.getCategorySeq(); - CategoryDto rDto = new CategoryDto(); - - Optional categoryEntityOptional = categoryRepository.findById(categorySeq); - - if (categoryEntityOptional.isPresent()) { - rDto = CategoryMapper.INSTANCE.categoryEntityToCategoryDto(categoryEntityOptional.get()); - } else { - throw new RuntimeException("Category 정보를 찾을 수 없습니다"); - } - - - log.info(this.getClass().getName() + ".findCategory End"); - return rDto; - } - - @Override - @Transactional - public List findAllCategory() throws Exception { - log.info(this.getClass().getName() + ".findAllCategory Start"); - - Iterable categoryEntityList = categoryRepository.findAll(); - - List categoryDtoList = new LinkedList<>(); - - categoryEntityList.forEach(categoryEntity -> { - categoryDtoList.add(CategoryMapper.INSTANCE.categoryEntityToCategoryDto(categoryEntity)); - }); - - log.info(this.getClass().getName() + ".findAllCategory End"); - return categoryDtoList; - } -} diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/CategoryServiceImpl.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/CategoryServiceImpl.java new file mode 100644 index 0000000..6a4b13a --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/CategoryServiceImpl.java @@ -0,0 +1,55 @@ +package com.submarket.itemservice.service.impl; + +import com.submarket.itemservice.dto.CategoryDto; +import com.submarket.itemservice.exception.CategoryException; +import com.submarket.itemservice.exception.result.CategoryExceptionResult; +import com.submarket.itemservice.jpa.CategoryRepository; +import com.submarket.itemservice.jpa.entity.CategoryEntity; +import com.submarket.itemservice.mapper.CategoryMapper; +import com.submarket.itemservice.service.CategoryService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@Slf4j +@RequiredArgsConstructor +public class CategoryServiceImpl implements CategoryService { + + private final CategoryRepository categoryRepository; + + @Override + public CategoryDto findCategory(CategoryDto categoryDto) throws Exception { + + log.debug(this.getClass().getName() + ".findCategoryInfo : " + categoryDto.getCategorySeq()); + + return CategoryMapper.INSTANCE.categoryEntityToCategoryDto( + categoryRepository.findById(categoryDto.getCategorySeq()) + .orElseThrow(() -> new CategoryException(CategoryExceptionResult.CATEGORY_NOT_FOUND))); + } + + @Override + public List findAllCategory() throws Exception { + log.info(this.getClass().getName() + ".findAllCategory Start"); + + List categoryEntityList = categoryRepository.findAll(); + + + + return getCategoryDtoListFromCategoryEntityList(categoryEntityList); + } + + // CategoryEntityList -> CategoryDtoList + private List getCategoryDtoListFromCategoryEntityList(final List categoryEntityList) { + + return categoryEntityList.stream().map(categoryEntity -> + CategoryMapper.INSTANCE.categoryEntityToCategoryDto(categoryEntity)).collect(Collectors.toList()); + + } +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewCheckService.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewCheckService.java deleted file mode 100644 index 321cb03..0000000 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewCheckService.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.submarket.itemservice.service.impl; - -import com.submarket.itemservice.dto.ItemDto; -import com.submarket.itemservice.dto.ItemReviewDto; -import com.submarket.itemservice.jpa.ItemRepository; -import com.submarket.itemservice.jpa.ItemReviewRepository; -import com.submarket.itemservice.jpa.entity.ItemEntity; -import com.submarket.itemservice.jpa.entity.ItemReviewEntity; -import com.submarket.itemservice.service.IItemReviewCheckService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.Optional; - -@Service -@RequiredArgsConstructor -@Slf4j - -public class ItemReviewCheckService implements IItemReviewCheckService { - private final ItemReviewRepository itemReviewRepository; - private final ItemRepository itemRepository; - - @Override - @Transactional - public boolean canCreateReview(ItemReviewDto itemReviewDto, int itemSeq) throws Exception { - log.info(this.getClass().getName() + ".canCreateReview Start!"); - String userId = itemReviewDto.getUserId(); - - Optional itemEntityOptional = itemRepository.findById(itemSeq); - ItemEntity item = itemEntityOptional.get(); - - Optional itemReviewEntity = itemReviewRepository.findByUserIdAndItem(userId, item); - - log.info(this.getClass().getName() + ".canCreateReview End!"); - - if (itemReviewEntity.isPresent()) { - log.info("이미 작성한 리뷰가 있음"); - return false; - } - return true; - } -} diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewCheckServiceImpl.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewCheckServiceImpl.java new file mode 100644 index 0000000..1eb4fee --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewCheckServiceImpl.java @@ -0,0 +1,41 @@ +package com.submarket.itemservice.service.impl; + +import com.submarket.itemservice.dto.ItemReviewDto; +import com.submarket.itemservice.exception.ItemException; +import com.submarket.itemservice.exception.ItemReviewException; +import com.submarket.itemservice.exception.result.ItemExceptionResult; +import com.submarket.itemservice.exception.result.ItemReviewExceptionResult; +import com.submarket.itemservice.jpa.ItemRepository; +import com.submarket.itemservice.jpa.ItemReviewRepository; +import com.submarket.itemservice.jpa.entity.ItemEntity; +import com.submarket.itemservice.service.ItemReviewCheckService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + + +@Service +@RequiredArgsConstructor +@Slf4j + +public class ItemReviewCheckServiceImpl implements ItemReviewCheckService { + private final ItemReviewRepository itemReviewRepository; + private final ItemRepository itemRepository; + + @Override + public boolean canCreateReview(ItemReviewDto itemReviewDto, int itemSeq) throws Exception { + log.debug(this.getClass().getName() + ".canCreateReview Start!"); + + ItemEntity item = itemRepository.findById(itemSeq) + .orElseThrow(() -> new ItemException(ItemExceptionResult.ITEM_NOT_FOUND)); + + + if (itemReviewRepository.findByUserIdAndItem(itemReviewDto.getUserId(), item).isPresent()) { + + throw new ItemReviewException(ItemReviewExceptionResult.ITEM_REVIEW_ALREADY_CREATED); + + } else { + return true; + } + } +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewService.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewServiceImpl.java similarity index 52% rename from ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewServiceImpl.java index 00b1452..8970ab7 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemReviewServiceImpl.java @@ -2,12 +2,15 @@ import com.submarket.itemservice.dto.ItemDto; import com.submarket.itemservice.dto.ItemReviewDto; +import com.submarket.itemservice.exception.ItemException; +import com.submarket.itemservice.exception.result.ItemExceptionResult; import com.submarket.itemservice.jpa.ItemReviewRepository; import com.submarket.itemservice.jpa.entity.ItemEntity; import com.submarket.itemservice.jpa.entity.ItemReviewEntity; import com.submarket.itemservice.mapper.ItemMapper; import com.submarket.itemservice.mapper.ItemReviewMapper; -import com.submarket.itemservice.service.IItemReviewService; +import com.submarket.itemservice.service.ItemReviewService; +import com.submarket.itemservice.service.ItemService; import com.submarket.itemservice.util.CmmUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,102 +20,98 @@ import java.util.LinkedList; import java.util.List; -@Service(value = "ItemReviewService") +@Service @Slf4j @RequiredArgsConstructor -public class ItemReviewService implements IItemReviewService { +public class ItemReviewServiceImpl implements ItemReviewService { private final ItemReviewRepository itemReviewRepository; private final ItemService itemService; @Override - public int saveReview(ItemReviewDto itemReviewDto, int itemSeq) throws Exception { - log.info(this.getClass().getName() + ".saveReview Start!"); - ItemDto itemDto = new ItemDto(); - itemDto.setItemSeq(itemSeq); + @Transactional(rollbackOn = Exception.class) + public void saveReview(ItemReviewDto itemReviewDto, int itemSeq) throws Exception { + log.debug(this.getClass().getName() + ".saveReview Start!"); int userAge = Integer.parseInt(itemReviewDto.getUserAge()); - ItemDto items = itemService.findItemInfo(itemDto); + ItemDto items = itemService.findItemInfo(ItemDto.builder().itemSeq(itemSeq).build()); ItemEntity itemEntity = ItemMapper.INSTANCE.itemDtoToItemEntity(items); itemReviewDto.setItem(itemEntity); - // 리뷰 생성 로직 - ItemReviewEntity itemReviewEntity; - itemReviewEntity = ItemReviewMapper.INSTANCE.itemReviewDtoToItemEntity(itemReviewDto); - itemService.upCountCustom(itemSeq, userAge, itemReviewDto.getReviewStar()); // readCount += Review Star - - itemReviewRepository.save(itemReviewEntity); + itemService.upReadCount(itemSeq, userAge, itemReviewDto.getReviewStar()); // readCount += Review Star + itemReviewRepository.save(ItemReviewMapper.INSTANCE.itemReviewDtoToItemEntity(itemReviewDto)); log.info(this.getClass().getName() + ".saveReview End!"); - return 1; + } @Override - public int modifyReview(ItemReviewDto itemReviewDto) throws Exception { + @Transactional(rollbackOn = Exception.class) + public void modifyReview(ItemReviewDto itemReviewDto) throws Exception { log.info(this.getClass().getName() + ".modifyReview Start!"); - int reviewSeq = itemReviewDto.getReviewSeq(); - String reviewContents = itemReviewDto.getReviewContents(); - String reviewDate = itemReviewDto.getReviewDate(); - int reviewStar = itemReviewDto.getReviewStar(); - - - itemReviewRepository.modifyItemReview(reviewContents, reviewDate, reviewStar, reviewSeq); + itemReviewRepository.modifyItemReview(itemReviewDto.getReviewContents(), + itemReviewDto.getReviewDate(), + itemReviewDto.getReviewStar(), + itemReviewDto.getReviewSeq()); log.info(this.getClass().getName() + ".modifyReview End!"); - return 1; + } @Override - public int deleteReview(ItemReviewDto itemReviewDto) throws Exception { + @Transactional(rollbackOn = Exception.class) + public void deleteReview(ItemReviewDto itemReviewDto) throws Exception { log.info(this.getClass().getName() + ".deleteReview Start!"); itemReviewRepository.deleteById(itemReviewDto.getReviewSeq()); log.info(this.getClass().getName() + ".deleteReview End!"); - return 0; } @Override - @Transactional - public List findAllReviewInItem(int itemSeq) throws Exception { + public List findAllReviewInItem(final int itemSeq) throws Exception { - ItemDto itemDto = new ItemDto(); - itemDto.setItemSeq(itemSeq); - ItemDto rDto = itemService.findItemInfo(itemDto); + ItemDto rDto = itemService.findItemInfo(ItemDto.builder().itemSeq(itemSeq).build()); if (rDto == null) { - throw new RuntimeException("상품 정보가 없습니다"); + throw new ItemException(ItemExceptionResult.ITEM_NOT_FOUND); } - List reviewDtoList = new LinkedList(); + List reviewDtoList = new LinkedList<>(); ItemEntity itemEntity = ItemMapper.INSTANCE.itemDtoToItemEntity(rDto); - List itemReviewEntityList = itemReviewRepository.findByItem(itemEntity); + List result = itemReviewRepository.findByItem(itemEntity); - itemReviewEntityList.forEach(itemReviewEntity -> { + result.forEach(itemReviewEntity -> { reviewDtoList.add(ItemReviewMapper.INSTANCE.itemReviewEntityToItemDto(itemReviewEntity)); }); + + return reviewDtoList; } @Override - @Transactional public List findAllReviewByUserId(String userId) throws Exception { log.info(this.getClass().getName() + ".findAllReviewByUserId Start!"); + List itemReviewDtoList = new LinkedList<>(); - List itemReviewEntityList = new LinkedList<>(); - itemReviewEntityList = itemReviewRepository.findAllByUserId(CmmUtil.nvl(userId)); + List result = itemReviewRepository.findAllByUserId(CmmUtil.nvl(userId)); - itemReviewEntityList.forEach(e -> { + if (result.isEmpty()) { + return new LinkedList(); + } + + result.forEach(e -> { itemReviewDtoList.add(ItemReviewMapper.INSTANCE.itemReviewEntityToItemDto(e)); }); log.info(this.getClass().getName() + ".findAllReviewByUserId End!"); + return itemReviewDtoList; } } \ No newline at end of file diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemService.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemServiceImpl.java similarity index 67% rename from ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemServiceImpl.java index 9193d15..2023c94 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/ItemServiceImpl.java @@ -1,16 +1,23 @@ package com.submarket.itemservice.service.impl; +import com.submarket.itemservice.client.UserServiceClient; import com.submarket.itemservice.dto.CategoryDto; import com.submarket.itemservice.dto.ItemDto; +import com.submarket.itemservice.exception.ItemException; +import com.submarket.itemservice.exception.result.ItemExceptionResult; import com.submarket.itemservice.jpa.CategoryRepository; import com.submarket.itemservice.jpa.ItemRepository; import com.submarket.itemservice.jpa.entity.CategoryEntity; import com.submarket.itemservice.jpa.entity.ItemEntity; import com.submarket.itemservice.mapper.CategoryMapper; import com.submarket.itemservice.mapper.ItemMapper; -import com.submarket.itemservice.service.IItemService; +import com.submarket.itemservice.service.CategoryService; +import com.submarket.itemservice.service.ItemService; +import com.submarket.itemservice.service.S3Service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.client.HttpStatusCodeException; @@ -20,24 +27,27 @@ import java.util.List; import java.util.Optional; +import static com.submarket.itemservice.constants.S3Constants.IMAGE_FOLDER_PATH; + @Slf4j @RequiredArgsConstructor -@Service("ItemService") -public class ItemService implements IItemService { +@Service +public class ItemServiceImpl implements ItemService { private final ItemRepository itemRepository; - private final CategoryService categoryService; - private final S3Service s3Service; + private final CategoryService categoryServiceImpl; + private final S3Service s3ServiceImpl; private final CategoryRepository categoryRepository; @Override - @Transactional - public int saveItem(ItemDto itemDto) throws Exception { + @Transactional(rollbackOn = ItemException.class) + public void saveItem(ItemDto itemDto) throws Exception { log.info(this.getClass().getName() + ".saveItem Start"); - CategoryDto categoryDto = new CategoryDto(); - categoryDto.setCategorySeq(itemDto.getCategorySeq()); - CategoryDto rDto = categoryService.findCategory(categoryDto); + CategoryDto rDto = categoryServiceImpl.findCategory(CategoryDto.builder() + .categorySeq(itemDto.getCategorySeq()).build()); + + String subImagePath = "/"; log.info("categoryName : " + rDto.getCategoryName()); @@ -45,21 +55,17 @@ public int saveItem(ItemDto itemDto) throws Exception { itemDto.setCategory(categoryEntity); - itemDto.setItemStatus(1); - itemDto.setReadCount20(0); - itemDto.setReadCount30(0); - itemDto.setReadCount40(0); - itemDto.setReadCountOther(0); + itemDto = setReadCountDefaultInItemDto(itemDto); log.info("MainImageSize : " + itemDto.getMainImage().getSize()); // 상품 이미지 등록 S3 Service (File, dirName) return : S3 Image Path /** Main Image 는 항상 NotNull */ - String mainImagePath = s3Service.uploadImageInS3(itemDto.getMainImage(), "images"); + final String mainImagePath = s3ServiceImpl.uploadImageInS3(itemDto.getMainImage(), IMAGE_FOLDER_PATH); if (itemDto.getSubImage() != null) { - subImagePath = s3Service.uploadImageInS3(itemDto.getSubImage(), "images"); + subImagePath = s3ServiceImpl.uploadImageInS3(itemDto.getSubImage(), IMAGE_FOLDER_PATH); } itemDto.setSubImagePath(subImagePath); @@ -72,13 +78,20 @@ public int saveItem(ItemDto itemDto) throws Exception { itemRepository.save(itemEntity); - log.info(this.getClass().getName() + ".saveItem End"); - return 1; + } + + private ItemDto setReadCountDefaultInItemDto(final ItemDto itemDto) { + itemDto.setItemStatus(1); + itemDto.setReadCount20(0); + itemDto.setReadCount30(0); + itemDto.setReadCount40(0); + itemDto.setReadCountOther(0); + + return itemDto; } @Override - @Transactional public ItemDto findItemInfo(ItemDto itemDto) throws Exception { log.info(this.getClass().getName() + "findItemInfo Start!"); @@ -88,14 +101,11 @@ public ItemDto findItemInfo(ItemDto itemDto) throws Exception { Optional itemEntityOptional = itemRepository.findById(itemSeq); - ItemDto rDto = ItemMapper.INSTANCE.itemEntityToItemDto(itemEntityOptional.get()); + log.info(this.getClass().getName() + ".findItemInfo End!"); - if (rDto == null) { - throw new RuntimeException("상품 정보를 찾을 수 없습니다"); - } - log.info(this.getClass().getName() + ".findItemInfo End!"); - return rDto; + return ItemMapper.INSTANCE.itemEntityToItemDto(itemEntityOptional.orElseThrow( + () -> new ItemException(ItemExceptionResult.ITEM_NOT_FOUND))); } @Override @@ -115,7 +125,7 @@ public List findAllItem() throws Exception { } @Override - @Transactional + @Transactional(rollbackOn = Exception.class) public int offItem(ItemDto itemDto) throws Exception { int itemSeq = itemDto.getItemSeq(); @@ -124,7 +134,7 @@ public int offItem(ItemDto itemDto) throws Exception { } @Override - @Transactional + @Transactional(rollbackOn = Exception.class) public int onItem(ItemDto itemDto) throws Exception { int itemSeq = itemDto.getItemSeq(); @@ -133,37 +143,36 @@ public int onItem(ItemDto itemDto) throws Exception { } @Override - @Transactional - public int modifyItem(ItemDto itemDto) throws Exception { + @Transactional(rollbackOn = Exception.class) + public void modifyItem(final ItemDto itemDtoReq) throws Exception { log.info(this.getClass().getName() + ".modifyItem Start!"); - int itemSeq = itemDto.getItemSeq(); + int itemSeq = itemDtoReq.getItemSeq(); - Optional itemEntityOptional = itemRepository.findById(itemSeq); - ItemEntity itemEntity = itemEntityOptional.get(); + ItemEntity itemEntity = itemRepository.findById(itemSeq) + .orElseThrow(() -> new ItemException(ItemExceptionResult.ITEM_NOT_FOUND)); - if (itemDto.getMainImage() != null) { - String mainImagePath = s3Service.uploadImageInS3(itemDto.getMainImage(), "images"); + if (itemDtoReq.getMainImage() != null) { + String mainImagePath = s3ServiceImpl.uploadImageInS3(itemDtoReq.getMainImage(), "images"); itemEntity.setMainImagePath(mainImagePath); } - if (itemDto.getSubImage() != null) { - String subImagePath = s3Service.uploadImageInS3(itemDto.getSubImage(), "images"); + if (itemDtoReq.getSubImage() != null) { + String subImagePath = s3ServiceImpl.uploadImageInS3(itemDtoReq.getSubImage(), "images"); itemEntity.setSubImagePath(subImagePath); } - Optional category = categoryRepository.findById(itemDto.getCategorySeq()); + Optional category = categoryRepository.findById(itemDtoReq.getCategorySeq()); - itemEntity.setItemTitle(itemDto.getItemTitle()); - itemEntity.setItemPrice(itemDto.getItemPrice()); - itemEntity.setItemCount(itemDto.getItemCount()); - itemEntity.setItemContents(itemDto.getItemContents()); + itemEntity.setItemTitle(itemDtoReq.getItemTitle()); + itemEntity.setItemPrice(itemDtoReq.getItemPrice()); + itemEntity.setItemCount(itemDtoReq.getItemCount()); + itemEntity.setItemContents(itemDtoReq.getItemContents()); itemEntity.setCategory(category.get()); itemRepository.save(itemEntity); log.info(this.getClass().getName() + ".modifyItem End!"); - return 1; } @Override @@ -200,10 +209,23 @@ public List findItemBySellerId(String sellerId) throws Exception { } } + + /** + * upReadCount + * + * @param itemSeq + * @param userAge ** @param readValue -> 해당 값 만큼 readCount 증가 or 1++ + * @throws Exception + */ + + @Override @Transactional @Async - public void upCount(int itemSeq, int userAge) throws Exception { + public void upReadCount(int itemSeq, int userAge) throws Exception { + + checkIsItemByItemSeq(itemSeq); + // 조회수 증가 int cUserAge = 0; cUserAge += userAge; @@ -225,9 +247,11 @@ public void upCount(int itemSeq, int userAge) throws Exception { @Override @Transactional @Async - public void upCountCustom(int itemSeq, int userAge, int readValue) throws Exception { + public void upReadCount(int itemSeq, int userAge, int readValue) throws Exception { log.info(this.getClass().getName() + "upCountCustom Start!"); + checkIsItemByItemSeq(itemSeq); + if (userAge > 0 && userAge <= 29) { itemRepository.increaseCustomReadCount20(itemSeq, readValue); @@ -243,4 +267,10 @@ public void upCountCustom(int itemSeq, int userAge, int readValue) throws Except log.info(this.getClass().getName() + "upCountCustom End!"); } + + private ItemEntity checkIsItemByItemSeq(final int itemSeq) { + + return itemRepository.findById(itemSeq).orElseThrow(() -> new ItemException(ItemExceptionResult.ITEM_NOT_FOUND)); + + } } diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaConsumerService.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaConsumerServiceImpl.java similarity index 92% rename from ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaConsumerService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaConsumerServiceImpl.java index 6c95255..5e5eee0 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaConsumerService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaConsumerServiceImpl.java @@ -6,7 +6,6 @@ import com.submarket.itemservice.dto.ItemDto; import com.submarket.itemservice.jpa.ItemRepository; import com.submarket.itemservice.jpa.entity.ItemEntity; -import com.submarket.itemservice.service.IKafkaConsumerService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; @@ -21,8 +20,8 @@ @Service @Slf4j @RequiredArgsConstructor -public class KafkaConsumerService implements IKafkaConsumerService { - private final ItemService itemService; +public class KafkaConsumerServiceImpl implements com.submarket.itemservice.service.KafkaConsumerService { + private final ItemServiceImpl itemService; private final ItemRepository itemRepository; /** * if there is new Topic --> update DB or something @@ -49,10 +48,12 @@ public void reduceItemCount(String kafkaMessage) throws Exception{ int itemSeq = Integer.parseInt(String.valueOf(map.get("itemSeq"))); int userAge = Integer.parseInt(String.valueOf(map.get("userAge"))); + log.info("itemSeq : " + itemSeq); + Optional itemEntityOptional = itemRepository.findById(itemSeq); if (itemEntityOptional.isPresent()) { itemRepository.reduceItemCount(itemSeq); - itemService.upCountCustom(itemSeq, userAge, 30); // 구독 시 ReadCount += 30 + itemService.upReadCount(itemSeq, userAge, 30); // 구독 시 ReadCount += 30 } } diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaProducerService.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaProducerServiceImpl.java similarity index 89% rename from ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaProducerService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaProducerServiceImpl.java index e49de12..c4f399e 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaProducerService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/KafkaProducerServiceImpl.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.submarket.itemservice.dto.ItemDto; -import com.submarket.itemservice.service.IKafkaProducerService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.core.KafkaTemplate; @@ -12,7 +11,7 @@ @Service @Slf4j @RequiredArgsConstructor -public class KafkaProducerService implements IKafkaProducerService { +public class KafkaProducerServiceImpl implements com.submarket.itemservice.service.KafkaProducerService { private final KafkaTemplate kafkaTemplate; @Override diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/S3Service.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/S3ServiceImpl.java similarity index 97% rename from ItemService/src/main/java/com/submarket/itemservice/service/impl/S3Service.java rename to ItemService/src/main/java/com/submarket/itemservice/service/impl/S3ServiceImpl.java index 7833c82..0057d54 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/S3Service.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/S3ServiceImpl.java @@ -3,7 +3,6 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.PutObjectRequest; -import com.submarket.itemservice.service.IS3Service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -21,7 +20,7 @@ @Service @Slf4j @RequiredArgsConstructor -public class S3Service implements IS3Service { +public class S3ServiceImpl implements com.submarket.itemservice.service.S3Service { private final AmazonS3Client amazonS3Client; private final Environment env; diff --git a/ItemService/src/main/java/com/submarket/itemservice/service/impl/SchedulerService.java b/ItemService/src/main/java/com/submarket/itemservice/service/impl/SchedulerServiceImpl.java similarity index 74% rename from ItemService/src/main/java/com/submarket/itemservice/service/impl/SchedulerService.java rename to ItemService/src/main/java/com/submarket/itemservice/service/impl/SchedulerServiceImpl.java index 2ae5e6b..32348f9 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/service/impl/SchedulerService.java +++ b/ItemService/src/main/java/com/submarket/itemservice/service/impl/SchedulerServiceImpl.java @@ -1,8 +1,6 @@ package com.submarket.itemservice.service.impl; import com.submarket.itemservice.dto.ItemDto; -import com.submarket.itemservice.jpa.ItemRepository; -import com.submarket.itemservice.util.DateUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; @@ -14,9 +12,9 @@ @Component @Slf4j @RequiredArgsConstructor -public class SchedulerService { - private final KafkaProducerService kafkaProducerService; - private final ItemService itemService; +public class SchedulerServiceImpl { + private final KafkaProducerServiceImpl kafkaProducerServiceImpl; + private final ItemServiceImpl itemService; @Scheduled(cron = "0 15 10 L * ?") // 매월 말 일 실행 @Async public void createSellerTotalSales() throws Exception { @@ -25,7 +23,7 @@ public void createSellerTotalSales() throws Exception { List itemDtoList = itemService.findAllItem(); log.info("itemDtoList Size : " + itemDtoList.size()); for (ItemDto itemDto : itemDtoList) { - kafkaProducerService.sendItemInfoToOrderService(itemDto); + kafkaProducerServiceImpl.sendItemInfoToOrderService(itemDto); } log.info(this.getClass().getName() + ".createSellerTotalSales End!"); diff --git a/ItemService/src/main/java/com/submarket/itemservice/util/TokenUtil.java b/ItemService/src/main/java/com/submarket/itemservice/util/TokenUtil.java index fa8957a..55258ae 100644 --- a/ItemService/src/main/java/com/submarket/itemservice/util/TokenUtil.java +++ b/ItemService/src/main/java/com/submarket/itemservice/util/TokenUtil.java @@ -24,6 +24,6 @@ public String getUserIdByToken(HttpHeaders headers) { log.info("getUserId End"); - return userId; + return String.valueOf(userId); } } diff --git a/ItemService/src/main/java/com/submarket/itemservice/vo/ItemInfoResponse.java b/ItemService/src/main/java/com/submarket/itemservice/vo/ItemInfoResponse.java new file mode 100644 index 0000000..3200ebc --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/vo/ItemInfoResponse.java @@ -0,0 +1,27 @@ +package com.submarket.itemservice.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ItemInfoResponse { + private int itemSeq; + private String sellerId; + private String itemTitle; + private String itemContents; + private int itemPrice; + private int itemCount; // 상품 수 + private int categorySeq; + private int itemStatus; // 활성화 + private int readCount20; + private int readCount30; + private int readCount40; + private int readCountOther; + private String mainImagePath; // DB에 저장되어 있는 이미지 정보 + private String subImagePath; +} diff --git a/ItemService/src/main/java/com/submarket/itemservice/vo/ItemLikedRequest.java b/ItemService/src/main/java/com/submarket/itemservice/vo/ItemLikedRequest.java new file mode 100644 index 0000000..6db952b --- /dev/null +++ b/ItemService/src/main/java/com/submarket/itemservice/vo/ItemLikedRequest.java @@ -0,0 +1,17 @@ +package com.submarket.itemservice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ItemLikedRequest { + private Integer itemSeq; +} diff --git a/ItemService/src/main/resources/ehcache.xml b/ItemService/src/main/resources/ehcache.xml new file mode 100644 index 0000000..1343393 --- /dev/null +++ b/ItemService/src/main/resources/ehcache.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/OrderService/pom.xml b/OrderService/pom.xml index 0a52e58..f0fa128 100644 --- a/OrderService/pom.xml +++ b/OrderService/pom.xml @@ -10,7 +10,7 @@ com.submarket orderservice - 1.0.0 + 2.0.0 OrderService OrderService @@ -76,11 +76,53 @@ spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + org.springframework.cloud spring-cloud-starter-bus-amqp + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-starter-zipkin + 2.2.3.RELEASE + + + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springdoc + springdoc-openapi-ui + 1.6.6 + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + diff --git a/OrderService/src/main/java/com/submarket/orderservice/OrderServiceApplication.java b/OrderService/src/main/java/com/submarket/orderservice/OrderServiceApplication.java index e64b969..fbe8785 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/OrderServiceApplication.java +++ b/OrderService/src/main/java/com/submarket/orderservice/OrderServiceApplication.java @@ -3,9 +3,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient +@EnableFeignClients public class OrderServiceApplication { public static void main(String[] args) { diff --git a/OrderService/src/main/java/com/submarket/orderservice/config/SwaggerConfig.java b/OrderService/src/main/java/com/submarket/orderservice/config/SwaggerConfig.java new file mode 100644 index 0000000..ecc9968 --- /dev/null +++ b/OrderService/src/main/java/com/submarket/orderservice/config/SwaggerConfig.java @@ -0,0 +1,44 @@ +package com.submarket.orderservice.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.customizers.OpenApiCustomiser; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public GroupedOpenApi publicApi() { + return GroupedOpenApi.builder() + .group("OrderService API") + .pathsToMatch("/**") + .addOpenApiCustomiser(buildSecurityOpenApi()) // JWT Setting Config + .build(); + } + + /** + * JWT Token Setting Config + * @return + */ + public OpenApiCustomiser buildSecurityOpenApi() { + return OpenApi -> OpenApi.addSecurityItem(new SecurityRequirement().addList("jwt token")) + .getComponents() + .addSecuritySchemes("jwt token", new SecurityScheme() + .name("Authorization") + .type(SecurityScheme.Type.HTTP) + .in(SecurityScheme.In.HEADER) + .bearerFormat("JWT") + .scheme("bearer")); + } + @Bean + public OpenAPI springShopOpenAPI() { + return new OpenAPI() + .info(new Info().title("SubMarket - ORDER-SERVICE API") + .description("SubMarket OrderService API 명세서").version("v2.0.0")); + } +} diff --git a/OrderService/src/main/java/com/submarket/orderservice/controller/MainController.java b/OrderService/src/main/java/com/submarket/orderservice/controller/MainController.java index 3a3fee2..fec4f4c 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/controller/MainController.java +++ b/OrderService/src/main/java/com/submarket/orderservice/controller/MainController.java @@ -1,17 +1,31 @@ package com.submarket.orderservice.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j @RequiredArgsConstructor +@Tag(name = "Health API", description = "Service Health Check EndPoint") public class MainController { private final Environment env; + + @Operation(summary = "상태 확인", description = "인증 정보 없이 접근할 수 있는 EndPoint") + @ApiResponses({ + @ApiResponse(responseCode = "503", description = "Eureka 서버에 미 등록"), + @ApiResponse(responseCode = "401", description = "Token 인증 실패"), + @ApiResponse(responseCode = "403", description = "Spring Security 인증 실패") + + }) @GetMapping("/health") public String health() { log.info("OrderService On"); diff --git a/OrderService/src/main/java/com/submarket/orderservice/controller/OrderController.java b/OrderService/src/main/java/com/submarket/orderservice/controller/OrderController.java index ffc1ca0..a2c3b54 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/controller/OrderController.java +++ b/OrderService/src/main/java/com/submarket/orderservice/controller/OrderController.java @@ -1,7 +1,12 @@ package com.submarket.orderservice.controller; import com.submarket.orderservice.dto.OrderDto; -import com.submarket.orderservice.service.impl.OrderService; +import com.submarket.orderservice.service.impl.OrderServiceImpl; +import com.submarket.orderservice.vo.OrderRequest; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -16,25 +21,45 @@ @Slf4j @RequiredArgsConstructor public class OrderController { - private final OrderService orderService; + private final OrderServiceImpl orderServiceImpl; - @PostMapping("/order") - public ResponseEntity insertOrder(@RequestBody OrderDto orderDto) throws Exception { + @Operation(summary = "주문 생성", description = "주문 생성", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "주문 생성 완료") + }) + @PostMapping("/orders") + @Timed(value = "order.save", longTask = true) + public ResponseEntity insertOrder(@RequestBody OrderRequest request) throws Exception { log.info(this.getClass().getName() + ".insertOrder Start!"); - orderService.insertOrder(orderDto); + orderServiceImpl.insertOrder(OrderDto.builder() + .orderId(request.getOrderId()) + .itemSeq(request.getItemSeq()) + .userId(request.getUserId()) + .userAddress(request.getUserAddress()) + .userAddress2(request.getUserAddress2()) + .sellerId(request.getSellerId()) + .orderDate(request.getOrderDate()) + .orderDateDetails(request.getOrderDateDetails()).build()); + log.info(this.getClass().getName() + ".insertOrder End!"); return ResponseEntity.status(HttpStatus.CREATED).body("주문 생성 완료"); } - @GetMapping("/order/user/{userId}") - public ResponseEntity> findOrderInfoByUserId(@PathVariable String userId) throws Exception { + + @Operation(summary = "사용자 주문 목록 조회", description = "사용자 아이디를 사용하여 주문 목록 조회", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "주문 목록 조회 성공") + }) + @GetMapping("/users/{userId}/orders") + @Timed(value = "user.order.findById", longTask = true) + public ResponseEntity> findOrderInfoByUserId(@PathVariable String userId) throws Exception { log.info(this.getClass().getName() + ".findOrderInfoByUserId Start!"); Map rMap = new HashMap<>(); - List orderDtoList = orderService.findAllOrderByUserId(userId); + List orderDtoList = orderServiceImpl.findAllOrderByUserId(userId); rMap.put("response", orderDtoList); @@ -45,14 +70,20 @@ public ResponseEntity> findOrderInfoByUserId(@PathVariable S return ResponseEntity.status(HttpStatus.OK).body(rMap); } - @GetMapping("/order/seller/{sellerId}") + + @Operation(summary = "판매자 주문 목록 조회", description = "판매자 아이디를 사용하여 주문 목록 조회", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "주문 목록 조회 성공") + }) + @GetMapping("sellers/{sellerId}/orders") + @Timed(value = "seller.order.findById", longTask = true) public ResponseEntity> findOrderInfoBySellerId(@PathVariable String sellerId) - throws Exception { + throws Exception { log.info(this.getClass().getName() + ".findOrderInfoBySellerId Start!"); Map rMap = new HashMap<>(); - List orderDtoList = orderService.findAllOrderBySellerId(sellerId); + List orderDtoList = orderServiceImpl.findAllOrderBySellerId(sellerId); rMap.put("response", orderDtoList); @@ -63,14 +94,25 @@ public ResponseEntity> findOrderInfoBySellerId(@PathVariable return ResponseEntity.status(HttpStatus.OK).body(rMap); } - @GetMapping("/order/order/{orderId}") + + @Operation(summary = "주문 정보 상세 조회", description = "주문 번호를 사용하여 주문 번호 상세 조회", tags = {"seller", "user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "주문 목록 조회 성공") + }) + @GetMapping("/orders/{orderId}") + @Timed(value = "order.findById", longTask = true) public ResponseEntity> findOrderInfoByOrderId(@PathVariable String orderId) throws Exception { Map rMap = new HashMap<>(); - OrderDto orderDto = orderService.findOneOrderByOrderId(orderId); + OrderDto orderDto = orderServiceImpl.findOneOrderByOrderId(orderId); rMap.put("response", orderDto); + if (orderDto == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(rMap); + } + + return ResponseEntity.ok().body(rMap); } } diff --git a/OrderService/src/main/java/com/submarket/orderservice/dto/OrderDto.java b/OrderService/src/main/java/com/submarket/orderservice/dto/OrderDto.java index e506c4c..a0a9178 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/dto/OrderDto.java +++ b/OrderService/src/main/java/com/submarket/orderservice/dto/OrderDto.java @@ -1,10 +1,12 @@ package com.submarket.orderservice.dto; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data +@Builder @NoArgsConstructor @AllArgsConstructor public class OrderDto { diff --git a/OrderService/src/main/java/com/submarket/orderservice/service/IKafkaConsumerService.java b/OrderService/src/main/java/com/submarket/orderservice/service/KafkaConsumerService.java similarity index 82% rename from OrderService/src/main/java/com/submarket/orderservice/service/IKafkaConsumerService.java rename to OrderService/src/main/java/com/submarket/orderservice/service/KafkaConsumerService.java index 538fdc1..95aa9fb 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/service/IKafkaConsumerService.java +++ b/OrderService/src/main/java/com/submarket/orderservice/service/KafkaConsumerService.java @@ -1,6 +1,6 @@ package com.submarket.orderservice.service; -public interface IKafkaConsumerService { +public interface KafkaConsumerService { void kafkaCreateOrder(String kafkaMessage) throws Exception; void kafkaGetItemInfoFromItemService(String kafkaMessage) throws Exception; diff --git a/OrderService/src/main/java/com/submarket/orderservice/service/IKafkaProducerService.java b/OrderService/src/main/java/com/submarket/orderservice/service/KafkaProducerService.java similarity index 78% rename from OrderService/src/main/java/com/submarket/orderservice/service/IKafkaProducerService.java rename to OrderService/src/main/java/com/submarket/orderservice/service/KafkaProducerService.java index c614255..01c059b 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/service/IKafkaProducerService.java +++ b/OrderService/src/main/java/com/submarket/orderservice/service/KafkaProducerService.java @@ -1,5 +1,5 @@ package com.submarket.orderservice.service; -public interface IKafkaProducerService { +public interface KafkaProducerService { void kafkaSendPriceToSellerService(int totalPrice, String date, String sellerId) throws Exception; } diff --git a/OrderService/src/main/java/com/submarket/orderservice/service/IOrderService.java b/OrderService/src/main/java/com/submarket/orderservice/service/OrderService.java similarity index 93% rename from OrderService/src/main/java/com/submarket/orderservice/service/IOrderService.java rename to OrderService/src/main/java/com/submarket/orderservice/service/OrderService.java index 2b006bb..20841cc 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/service/IOrderService.java +++ b/OrderService/src/main/java/com/submarket/orderservice/service/OrderService.java @@ -4,7 +4,7 @@ import java.util.List; -public interface IOrderService { +public interface OrderService { int insertOrder(OrderDto orderDto) throws Exception; diff --git a/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaConsumerService.java b/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaConsumerServiceImpl.java similarity index 84% rename from OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaConsumerService.java rename to OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaConsumerServiceImpl.java index 1da4b01..5e806bc 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaConsumerService.java +++ b/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaConsumerServiceImpl.java @@ -4,13 +4,12 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.submarket.orderservice.dto.OrderDto; -import com.submarket.orderservice.service.IKafkaConsumerService; +import com.submarket.orderservice.service.KafkaConsumerService; import com.submarket.orderservice.util.CmmUtil; import com.submarket.orderservice.util.DateUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.annotation.KafkaListener; -import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Service; import java.util.HashMap; @@ -19,9 +18,9 @@ @Service @Slf4j @RequiredArgsConstructor -public class KafkaConsumerService implements IKafkaConsumerService { - private final OrderService orderService; - private final KafkaProducerService kafkaProducerService; +public class KafkaConsumerServiceImpl implements KafkaConsumerService { + private final OrderServiceImpl orderServiceImpl; + private final KafkaProducerServiceImpl kafkaProducerServiceImpl; @KafkaListener(topics = "sub") @Override @@ -48,7 +47,7 @@ public void kafkaCreateOrder(String kafkaMessage) throws Exception { orderDto.setUserAddress(String.valueOf(map.get("userAddress"))); orderDto.setUserAddress2(CmmUtil.nvl(String.valueOf(map.get("userAddress2")))); - orderService.insertOrder(orderDto); + orderServiceImpl.insertOrder(orderDto); log.info(this.getClass().getName() + ".kafkaCreateOrder Start!"); } @@ -76,10 +75,10 @@ public void kafkaGetItemInfoFromItemService(String kafkaMessage) throws Exceptio OrderDto orderDto = new OrderDto(); orderDto.setItemSeq(itemSeq); - int totalPrice = orderService.totalPriceByItemSeq(orderDto, itemPrice); + int totalPrice = orderServiceImpl.totalPriceByItemSeq(orderDto, itemPrice); String date = DateUtil.getDateTime("yyyyMM"); - kafkaProducerService.kafkaSendPriceToSellerService(totalPrice, date, sellerId); + kafkaProducerServiceImpl.kafkaSendPriceToSellerService(totalPrice, date, sellerId); log.info(this.getClass().getName() + ".kafkaGetItemInfoFromItemService End!"); diff --git a/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaProducerService.java b/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaProducerServiceImpl.java similarity index 91% rename from OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaProducerService.java rename to OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaProducerServiceImpl.java index afddff8..3d65bd4 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaProducerService.java +++ b/OrderService/src/main/java/com/submarket/orderservice/service/impl/KafkaProducerServiceImpl.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.submarket.orderservice.service.IKafkaProducerService; +import com.submarket.orderservice.service.KafkaProducerService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.core.KafkaTemplate; @@ -14,7 +14,7 @@ @Service @Slf4j @RequiredArgsConstructor -public class KafkaProducerService implements IKafkaProducerService { +public class KafkaProducerServiceImpl implements KafkaProducerService { private final KafkaTemplate kafkaTemplate; diff --git a/OrderService/src/main/java/com/submarket/orderservice/service/impl/OrderService.java b/OrderService/src/main/java/com/submarket/orderservice/service/impl/OrderServiceImpl.java similarity index 95% rename from OrderService/src/main/java/com/submarket/orderservice/service/impl/OrderService.java rename to OrderService/src/main/java/com/submarket/orderservice/service/impl/OrderServiceImpl.java index 1ff047d..b85e40f 100644 --- a/OrderService/src/main/java/com/submarket/orderservice/service/impl/OrderService.java +++ b/OrderService/src/main/java/com/submarket/orderservice/service/impl/OrderServiceImpl.java @@ -2,9 +2,8 @@ import com.submarket.orderservice.dto.OrderDto; import com.submarket.orderservice.mapper.impl.OrderMapper; -import com.submarket.orderservice.service.IOrderService; +import com.submarket.orderservice.service.OrderService; import com.submarket.orderservice.util.DateUtil; -import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -16,7 +15,7 @@ @Slf4j @Service(value = "OrderService") @RequiredArgsConstructor -public class OrderService implements IOrderService { +public class OrderServiceImpl implements OrderService { private final OrderMapper orderMapper; @Override diff --git a/OrderService/src/main/java/com/submarket/orderservice/vo/OrderRequest.java b/OrderService/src/main/java/com/submarket/orderservice/vo/OrderRequest.java new file mode 100644 index 0000000..1194bcb --- /dev/null +++ b/OrderService/src/main/java/com/submarket/orderservice/vo/OrderRequest.java @@ -0,0 +1,29 @@ +package com.submarket.orderservice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class OrderRequest { + private String orderId; + + private int itemSeq; + + private String userId; + + private String sellerId; + + private String userAddress; + + private String userAddress2; + + private String orderDateDetails; + + private String orderDate; +} diff --git a/OrderService/src/test/java/com/submarket/orderservice/OrderServiceApplicationTests.java b/OrderService/src/test/java/com/submarket/orderservice/OrderServiceImplApplicationTests.java similarity index 82% rename from OrderService/src/test/java/com/submarket/orderservice/OrderServiceApplicationTests.java rename to OrderService/src/test/java/com/submarket/orderservice/OrderServiceImplApplicationTests.java index 4fa14a3..68ba36a 100644 --- a/OrderService/src/test/java/com/submarket/orderservice/OrderServiceApplicationTests.java +++ b/OrderService/src/test/java/com/submarket/orderservice/OrderServiceImplApplicationTests.java @@ -4,7 +4,7 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class OrderServiceApplicationTests { +class OrderServiceImplApplicationTests { @Test void contextLoads() { diff --git a/README.md b/README.md deleted file mode 100644 index cd60bb8..0000000 --- a/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Sub Market (e-commerce) - -### MSA기반의 구독 상품을 판매 및 구매할 수 있는 E-commerce 서비스입니다. - -## Description - -> 2022.05 - 2022.06 - -### 적용 기술 - -
- - -
- -
- -
- - - -
-
- -> _**Other : JWT, JPA, Load Balancer(Eureka), Gateway(Spring Cloud Gateway), REST-API, JSP, Spring Cloud Config**_ -
-
- -https://user-images.githubusercontent.com/63443366/186517339-aa4913a6-1abe-4379-97b9-59f6e19896d8.mp4 - -
- -### Summary - -![Summary](https://user-images.githubusercontent.com/63443366/175762264-d15dfadd-e097-4dd9-8294-c86e65a15691.png) - -* **USER** - * 구독 상품 구매 - -* **SELLER** - * 상품 판매, 매출 정보 확인 및 사용자 주문 확인 - -* **Background** - * 매달 사용자 구독을 갱신하는 기능 - * 판매자의 월별 매출 정보를 매월 말 저장 - * EDA (Event-driven architecture) with Kafka - -### Kafka - -![Kafka](https://user-images.githubusercontent.com/63443366/175763046-816a1b35-9df7-4249-965b-fa7ce7f31ccc.png) - -* **상품 구독** - * 사용자가 상품 구독 시 상품 수량 감소 및 주문 생성 - * https://github.com/JeongSeonggil/SubMarketWithGit/issues/129 - -* **상품 구독 취소** - * 사용자가 상품 구독을 취소하면 수량 증가 - * https://github.com/JeongSeonggil/SubMarketWithGit/issues/132 - -* **매출 정보 분석 및 적재** - * 매월 말 판매자 서비스에 월 별 판매량을 적재 및 분석 시 사용 - * https://github.com/JeongSeonggil/SubMarketWithGit/issues/137 - -* **사업자 탈퇴 시 판매 중인 상품 비활성화** - * 판매자가 탈퇴한다면 판매자가 판매중이였던 상품 비활성화 - * https://github.com/JeongSeonggil/SubMarketWithGit/issues/141 - -## ERD -![image](https://user-images.githubusercontent.com/63443366/184500117-3e225474-5937-4399-8c10-ab70110219e3.png) diff --git a/SellerService/pom.xml b/SellerService/pom.xml index 8df3f1a..6d2828c 100644 --- a/SellerService/pom.xml +++ b/SellerService/pom.xml @@ -10,7 +10,7 @@ com.submarket sellerservice - 1.0.0 + 2.0.0 SellerService SellerService @@ -133,6 +133,42 @@ org.springframework.kafka spring-kafka + + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-starter-zipkin + 2.2.3.RELEASE + + + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springdoc + springdoc-openapi-ui + 1.6.6 +
diff --git a/SellerService/src/main/java/com/submarket/sellerservice/SellerServiceApplication.java b/SellerService/src/main/java/com/submarket/sellerservice/SellerServiceApplication.java index 73517b1..a20771e 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/SellerServiceApplication.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/SellerServiceApplication.java @@ -3,6 +3,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -10,6 +11,7 @@ @SpringBootApplication @EnableEurekaClient +@EnableFeignClients public class SellerServiceApplication { public static void main(String[] args) { diff --git a/SellerService/src/main/java/com/submarket/sellerservice/config/Resilience4JConfig.java b/SellerService/src/main/java/com/submarket/sellerservice/config/Resilience4JConfig.java new file mode 100644 index 0000000..4cc42ff --- /dev/null +++ b/SellerService/src/main/java/com/submarket/sellerservice/config/Resilience4JConfig.java @@ -0,0 +1,32 @@ +package com.submarket.sellerservice.config; + +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; +import org.springframework.cloud.client.circuitbreaker.Customizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Duration; + +@Configuration +public class Resilience4JConfig { + + @Bean + public Customizer globalCustomConfiguration() { + CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() + .failureRateThreshold(4) // Open or False 100 중 4 번 실패 시 Open + .waitDurationInOpenState(Duration.ofMillis(1000)) // Open time (1s) + .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) // 수 or 시간 기반 + .slidingWindowSize(2).build(); + + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() + .timeoutDuration(Duration.ofSeconds(4)).build(); + + return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id) + .timeLimiterConfig(timeLimiterConfig) + .circuitBreakerConfig(circuitBreakerConfig).build()); + + } +} diff --git a/SellerService/src/main/java/com/submarket/sellerservice/config/SwaggerConfig.java b/SellerService/src/main/java/com/submarket/sellerservice/config/SwaggerConfig.java new file mode 100644 index 0000000..6647c56 --- /dev/null +++ b/SellerService/src/main/java/com/submarket/sellerservice/config/SwaggerConfig.java @@ -0,0 +1,44 @@ +package com.submarket.sellerservice.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.customizers.OpenApiCustomiser; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public GroupedOpenApi publicApi() { + return GroupedOpenApi.builder() + .group("SellerServiceAPI") + .pathsToMatch("/**") + .addOpenApiCustomiser(buildSecurityOpenApi()) // JWT Setting Config + .build(); + } + + /** + * JWT Token Setting Config + * @return + */ + public OpenApiCustomiser buildSecurityOpenApi() { + return OpenApi -> OpenApi.addSecurityItem(new SecurityRequirement().addList("jwt token")) + .getComponents() + .addSecuritySchemes("jwt token", new SecurityScheme() + .name("Authorization") + .type(SecurityScheme.Type.HTTP) + .in(SecurityScheme.In.HEADER) + .bearerFormat("JWT") + .scheme("bearer")); + } + @Bean + public OpenAPI springShopOpenAPI() { + return new OpenAPI() + .info(new Info().title("SubMarket - SELLER-SERVICE API") + .description("SubMarket SellerService 명세서").version("v2.0.0")); + } +} diff --git a/SellerService/src/main/java/com/submarket/sellerservice/controller/MainController.java b/SellerService/src/main/java/com/submarket/sellerservice/controller/MainController.java index 4de6fbb..383a457 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/controller/MainController.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/controller/MainController.java @@ -1,6 +1,11 @@ package com.submarket.sellerservice.controller; import com.submarket.sellerservice.dto.BusinessIdApiDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; @@ -10,6 +15,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @@ -17,14 +23,20 @@ @Slf4j @RestController +@RequiredArgsConstructor +@Tag(name = "Health API", description = "서버 상태 확인") public class MainController { - private Environment env; + private final Environment env; - @Autowired - public MainController(Environment env) { - this.env = env; - } + + @Operation(summary = "상태 확인", description = "인증 정보 없이 접근할 수 있는 EndPoint") + @ApiResponses({ + @ApiResponse(responseCode = "503", description = "Eureka 서버에 미 등록"), + @ApiResponse(responseCode = "401", description = "Token 인증 실패"), + @ApiResponse(responseCode = "403", description = "Spring Security 인증 실패") + + }) @GetMapping("/health") public String health() { log.info("SellerService On"); diff --git a/SellerService/src/main/java/com/submarket/sellerservice/controller/SalesController.java b/SellerService/src/main/java/com/submarket/sellerservice/controller/SalesController.java index c602658..7d158aa 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/controller/SalesController.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/controller/SalesController.java @@ -3,6 +3,11 @@ import com.submarket.sellerservice.dto.SalesDto; import com.submarket.sellerservice.service.impl.SalesService; import com.submarket.sellerservice.util.TokenUtil; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -10,6 +15,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; @@ -19,11 +25,17 @@ @RestController @Slf4j @RequiredArgsConstructor +@Tag(name = "판매자 매출 정보 API", description = "사용자 매출 정보 관련 APi") public class SalesController { private final SalesService salesService; private final TokenUtil tokenUtil; - @GetMapping("/seller/sales") + @Operation(summary = "모든 매출 정보 조회", description = "판매자 아이디를 기준으로 매출 목록 조회", tags = {"sales"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "매출 정보 조회 성공") + }) + @GetMapping("/sellers/sales") + @Timed(value = "seller.sales.findById", longTask = true) public ResponseEntity> findAllSalesDtoBySellerId(@RequestHeader HttpHeaders headers) throws Exception { log.info(this.getClass().getName() + ".findAllSalesDtoBySellerId Start!"); String sellerId = tokenUtil.getUserIdByToken(headers); diff --git a/SellerService/src/main/java/com/submarket/sellerservice/controller/SellerController.java b/SellerService/src/main/java/com/submarket/sellerservice/controller/SellerController.java index 14598dc..b3e5969 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/controller/SellerController.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/controller/SellerController.java @@ -8,6 +8,11 @@ import com.submarket.sellerservice.vo.RequestChangePassword; import com.submarket.sellerservice.vo.RequestSellerInfo; import com.submarket.sellerservice.vo.ResponseSellerInfo; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpEntity; @@ -24,13 +29,19 @@ @RestController @RequiredArgsConstructor @Slf4j +@Tag(name = "판매자 정보 API", description = "판매자 정보 관련 API") public class SellerController { private final SellerService sellerService; private final TokenUtil tokenUtil; private final SellerCheckService sellerCheckService; + @Operation(summary = "판매자 정보 조회", description = "Token 값을 사용하여 사용자 정보 조회", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "매출 정보 조회 성공") + }) @GetMapping("/seller") + @Timed(value = "seller.findById", longTask = true) public ResponseEntity getSellerInfo(@RequestHeader HttpHeaders headers) throws Exception { log.info(this.getClass().getName() + ".getSellerInfo Start!"); SellerDto pDto = new SellerDto(); @@ -55,7 +66,16 @@ public ResponseEntity getSellerInfo(@RequestHeader HttpHeade return ResponseEntity.ok().body(sellerInfo); } + + @Operation(summary = "판매자 정보 등록", description = "판매자 정보 등록", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "회원가입 성공"), + @ApiResponse(responseCode = "409", description = "중복된 아이디"), + @ApiResponse(responseCode = "409", description = "중복된 이메일"), + @ApiResponse(responseCode = "400", description = "회원가입 실패") + }) @PostMapping("/sellers") + @Timed(value = "seller.save", longTask = true) public ResponseEntity createSeller(@RequestBody RequestSellerInfo sellerInfo) throws Exception { log.info(this.getClass().getName() + ".createSeller Start!"); @@ -75,7 +95,14 @@ public ResponseEntity createSeller(@RequestBody RequestSellerInfo seller } } + + @Operation(summary = "판매자 정보 수정", description = "판매자 정보 수정", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "판매자 정보 수정 성공"), + @ApiResponse(responseCode = "500", description = "판매자 정보 수정 실패") + }) @PostMapping("/sellers/modify") + @Timed(value = "seller.modify", longTask = true) public ResponseEntity modifySellerInfo(@RequestBody SellerDto sellerDto, @RequestHeader HttpHeaders headers) throws Exception { log.info(this.getClass().getName() + ".modifySellerInfo Start!"); @@ -95,7 +122,13 @@ public ResponseEntity modifySellerInfo(@RequestBody SellerDto sellerDto return ResponseEntity.ok().body("수정 실패 (500)"); } + + @Operation(summary = "판매자 정보 삭제", description = "비밀번호 인증 후 판매자 정보 삭제", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "판매자 정보 삭제 성공") + }) @PostMapping("/sellers/drop") + @Timed(value = "seller.drop", longTask = true) public ResponseEntity deleteSeller(@RequestHeader HttpHeaders headers, @RequestBody RequestSellerInfo requestSellerInfo) throws Exception { log.info(this.getClass().getName() + ".deleteSeller Start!"); @@ -117,7 +150,13 @@ public ResponseEntity deleteSeller(@RequestHeader HttpHeaders headers, * <------------------------>아이디 찾기 with UserEmail * 만약 Email 이 같다면 아이디 정보 일부를 제공 */ + @Operation(summary = "판매자 아이디 찾기", description = "이메일 인증 후 아이디 찾기", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "판매자 아이디 정보 성공"), + @ApiResponse(responseCode = "404", description = "이메일과 일치하는 판매자 정보를 찾을 수 없음"), + }) @GetMapping("/sellers/find-id/{sellerEmail}") + @Timed(value = "seller.find.id", longTask = true) public ResponseEntity findSellerId(@PathVariable String sellerEmail) throws Exception { log.info("-------------------- > " + this.getClass().getName() + "findSellerId Start!"); SellerDto sellerDto = new SellerDto(); @@ -135,7 +174,13 @@ public ResponseEntity findSellerId(@PathVariable String sellerEmail) thr return ResponseEntity.status(HttpStatus.OK).body(sellerId); } + + @Operation(summary = "판매자 비밀번호 변경", description = "이전 비밀번호가 일치한다면 비밀번호 변경 진행", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "판매자 비밀번호 변경 성공") + }) @PatchMapping("/sellers/change-password") + @Timed(value = "seller.change.password", longTask = true) public ResponseEntity changePassword(@RequestHeader HttpHeaders headers, @RequestBody RequestChangePassword requestChangePassword) throws Exception { log.info(this.getClass().getName() + ".changePassword Start!"); @@ -155,8 +200,15 @@ public ResponseEntity changePassword(@RequestHeader HttpHeaders headers, return ResponseEntity.ok().body("비밀번호 변경 성공"); } + + @Operation(summary = "사업자 번호 유효성 검사", description = "Open API를 이용하여 정보가 있다면 OK", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "유효한 사업자"), + @ApiResponse(responseCode = "400", description = "유효하지 않는 사업자 번호") + }) // 사업자 번호 유효성 검사 @GetMapping("/seller/business/{businessId}") + @Timed(value = "seller.check.businessId", longTask = true) public ResponseEntity> checkBusinessId(@PathVariable String businessId) throws Exception { log.info(this.getClass().getName() + ".checkBusinessId Start!"); diff --git a/SellerService/src/main/java/com/submarket/sellerservice/dto/BusinessIdApiDto.java b/SellerService/src/main/java/com/submarket/sellerservice/dto/BusinessIdApiDto.java index ccd7523..b607311 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/dto/BusinessIdApiDto.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/dto/BusinessIdApiDto.java @@ -9,5 +9,6 @@ public class BusinessIdApiDto { private int request_cnt; private String status_code; + private List> data; } diff --git a/SellerService/src/main/java/com/submarket/sellerservice/dto/SalesDto.java b/SellerService/src/main/java/com/submarket/sellerservice/dto/SalesDto.java index 87cf0f7..cf1b491 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/dto/SalesDto.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/dto/SalesDto.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import javax.persistence.*; +import java.util.List; @Data @NoArgsConstructor @@ -16,4 +17,6 @@ public class SalesDto { private int value; private SellerEntity seller; + + private List response; } diff --git a/SellerService/src/main/java/com/submarket/sellerservice/jpa/entity/SalesEntity.java b/SellerService/src/main/java/com/submarket/sellerservice/jpa/entity/SalesEntity.java index 3ed7d62..cf2cd09 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/jpa/entity/SalesEntity.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/jpa/entity/SalesEntity.java @@ -14,7 +14,7 @@ @NoArgsConstructor @AllArgsConstructor @Builder -@JsonIgnoreProperties({"seller"}) +@ToString(exclude={"seller"}) public class SalesEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/SellerService/src/main/java/com/submarket/sellerservice/mapper/SalesMapper.java b/SellerService/src/main/java/com/submarket/sellerservice/mapper/SalesMapper.java index 4fe85ca..4325840 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/mapper/SalesMapper.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/mapper/SalesMapper.java @@ -9,9 +9,7 @@ @Mapper public interface SalesMapper { SalesMapper INSTANCE = Mappers.getMapper(SalesMapper.class); - - SalesEntity salesDtoToSalesEntity(SalesDto salesDto); - @Mapping(target = "seller", ignore = true) SalesDto salesEntityToSalesDto(SalesEntity salesEntity); + SalesEntity salesDtoToSalesEntity(SalesDto salesDto); } diff --git a/SellerService/src/main/java/com/submarket/sellerservice/mapper/SellerMapper.java b/SellerService/src/main/java/com/submarket/sellerservice/mapper/SellerMapper.java index 3a8c263..d977c1e 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/mapper/SellerMapper.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/mapper/SellerMapper.java @@ -12,13 +12,9 @@ @Mapper public interface SellerMapper { SellerMapper INSTANCE = Mappers.getMapper(SellerMapper.class); - @Mapping(source = "sellerEncPassword", target = "sellerPassword") SellerEntity SellerDtoToSellerEntity(SellerDto SellerDto); - SellerDto sellerEntityToSellerDto(SellerEntity sellEntity); - SellerDto requestSellerInfoToSellerDto(RequestSellerInfo requestSellerInfo); - ResponseSellerInfo SellerDtoToResponseSellerInfo(SellerDto SellerDto); } diff --git a/SellerService/src/main/java/com/submarket/sellerservice/security/WebSecurity.java b/SellerService/src/main/java/com/submarket/sellerservice/security/WebSecurity.java index deeff79..2501103 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/security/WebSecurity.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/security/WebSecurity.java @@ -22,9 +22,7 @@ public class WebSecurity extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); - - http.authorizeRequests().antMatchers("/**") - .hasIpAddress(env.getProperty("gateway.ip")) // IP + http.authorizeRequests().antMatchers("/**").permitAll() .and() .addFilter(getAuthenticationFilter()); // Add Filter diff --git a/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestChangePassword.java b/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestChangePassword.java index 28109db..11bc7cf 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestChangePassword.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestChangePassword.java @@ -1,5 +1,6 @@ package com.submarket.sellerservice.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data diff --git a/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestLogin.java b/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestLogin.java index 421066b..3311a1b 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestLogin.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestLogin.java @@ -1,5 +1,6 @@ package com.submarket.sellerservice.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data diff --git a/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestSellerInfo.java b/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestSellerInfo.java index e4b71ad..284a65a 100644 --- a/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestSellerInfo.java +++ b/SellerService/src/main/java/com/submarket/sellerservice/vo/RequestSellerInfo.java @@ -1,5 +1,6 @@ package com.submarket.sellerservice.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -12,30 +13,15 @@ @AllArgsConstructor @NoArgsConstructor public class RequestSellerInfo { - private int sellerSeq; - - @NotNull(message = "아이디를 입력해 주세요") private String sellerId; - - @Size(min = 8, max = 40, message = "비밀번호는 8 ~ 40 글자 사이로 입력해주세요") private String sellerPassword; - - @NotNull(message = "사업자 번호를 입력해주세요") private String businessId; - - @NotNull(message = "연락처를 입력해주세요") private String sellerPn; - - @Email(message = "Email 형식이 아닙니다") private String sellerEmail; - - @NotNull(message = "주소를 입력해주세요") private String sellerAddress; private String sellerAddress2; - private String sellerHome; - private String sellerName; } diff --git a/UserService/.gitignore b/UserService/.gitignore index 9fbbc11..21fcd4e 100644 --- a/UserService/.gitignore +++ b/UserService/.gitignore @@ -33,4 +33,4 @@ build/ .vscode/ /src/main/resources/application.yml -/src/main/resources/bootstrap.yml +/src/main/resources/bootstrap.yml \ No newline at end of file diff --git a/UserService/pom.xml b/UserService/pom.xml index efb2dff..1df62f3 100644 --- a/UserService/pom.xml +++ b/UserService/pom.xml @@ -10,7 +10,7 @@ com.submarket userservice - 1.0.0 + 2.0.0 UserService UserService @@ -175,6 +175,42 @@ spring-kafka + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-starter-zipkin + 2.2.3.RELEASE + + + + + io.micrometer + micrometer-registry-prometheus + + + + + org.springdoc + springdoc-openapi-ui + 1.6.6 + +
diff --git a/UserService/src/main/java/com/submarket/userservice/UserServiceApplication.java b/UserService/src/main/java/com/submarket/userservice/UserServiceApplication.java index 3715d6a..249a185 100644 --- a/UserService/src/main/java/com/submarket/userservice/UserServiceApplication.java +++ b/UserService/src/main/java/com/submarket/userservice/UserServiceApplication.java @@ -1,13 +1,17 @@ package com.submarket.userservice; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.Logger; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @SpringBootApplication @EnableEurekaClient +@EnableFeignClients public class UserServiceApplication { public static void main(String[] args) { @@ -19,4 +23,15 @@ public BCryptPasswordEncoder bCryptPasswordEncoder(){ return new BCryptPasswordEncoder(); } + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper(); + } + + + @Bean + public Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } + } diff --git a/UserService/src/main/java/com/submarket/userservice/client/ItemServiceClient.java b/UserService/src/main/java/com/submarket/userservice/client/ItemServiceClient.java new file mode 100644 index 0000000..3ea1c92 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/client/ItemServiceClient.java @@ -0,0 +1,20 @@ +package com.submarket.userservice.client; + +import com.submarket.userservice.dto.ItemDto; +import com.submarket.userservice.vo.ItemInfoResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.Map; + +@FeignClient(name = "ITEM-SERVICE") +public interface ItemServiceClient { + @GetMapping("/items/{itemSeq}") // 상품이 확인 + ResponseEntity isItem(@PathVariable int itemSeq); + + + @GetMapping("/circuit/items/{itemSeq}") // 상품이 확인 + ResponseEntity findOneItem(@PathVariable int itemSeq); +} diff --git a/UserService/src/main/java/com/submarket/userservice/config/Resilience4JConfig.java b/UserService/src/main/java/com/submarket/userservice/config/Resilience4JConfig.java new file mode 100644 index 0000000..db97d4c --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/config/Resilience4JConfig.java @@ -0,0 +1,32 @@ +package com.submarket.userservice.config; + +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; +import org.springframework.cloud.client.circuitbreaker.Customizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Duration; + +@Configuration +public class Resilience4JConfig { + + @Bean + public Customizer globalCustomConfiguration() { + CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() + .failureRateThreshold(4) // Open or False 100 중 4 번 실패 시 Open + .waitDurationInOpenState(Duration.ofMillis(1000)) // Open time (1s) + .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED) // 수 or 시간 기반 + .slidingWindowSize(2).build(); + + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() + .timeoutDuration(Duration.ofSeconds(4)).build(); + + return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id) + .timeLimiterConfig(timeLimiterConfig) + .circuitBreakerConfig(circuitBreakerConfig).build()); + + } +} diff --git a/UserService/src/main/java/com/submarket/userservice/config/SwaggerConfig.java b/UserService/src/main/java/com/submarket/userservice/config/SwaggerConfig.java new file mode 100644 index 0000000..4d53cf3 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/config/SwaggerConfig.java @@ -0,0 +1,44 @@ +package com.submarket.userservice.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.customizers.OpenApiCustomiser; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public GroupedOpenApi publicApi() { + return GroupedOpenApi.builder() + .group("UserServiceAPI") + .pathsToMatch("/**") + .addOpenApiCustomiser(buildSecurityOpenApi()) // JWT Setting Config + .build(); + } + + /** + * JWT Token Setting Config + * @return + */ + public OpenApiCustomiser buildSecurityOpenApi() { + return OpenApi -> OpenApi.addSecurityItem(new SecurityRequirement().addList("jwt token")) + .getComponents() + .addSecuritySchemes("jwt token", new SecurityScheme() + .name("Authorization") + .type(SecurityScheme.Type.HTTP) + .in(SecurityScheme.In.HEADER) + .bearerFormat("JWT") + .scheme("bearer")); + } + @Bean + public OpenAPI springShopOpenAPI() { + return new OpenAPI() + .info(new Info().title("SubMarket - USER-SERVICE API") + .description("SubMarket UserService API 명세서").version("v2.0.0")); + } +} diff --git a/UserService/src/main/java/com/submarket/userservice/constants/KafkaConstants.java b/UserService/src/main/java/com/submarket/userservice/constants/KafkaConstants.java new file mode 100644 index 0000000..7ef7708 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/constants/KafkaConstants.java @@ -0,0 +1,16 @@ +package com.submarket.userservice.constants; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class KafkaConstants { + public final static String TOPIC_NAME_SUB = "sub"; + public final static String TOPIC_NAME_CANCEL_SUB = "sub-cancel"; + + public final static String TOPIC_NAME_ITEM_LIKED = "item-liked"; + + public final static String TOPIC_NAME_CANCEL_ITEM_LIKED = "item-liked-cancel"; +} diff --git a/UserService/src/main/java/com/submarket/userservice/controller/MainController.java b/UserService/src/main/java/com/submarket/userservice/controller/MainController.java index 435abfd..a8dc0c8 100644 --- a/UserService/src/main/java/com/submarket/userservice/controller/MainController.java +++ b/UserService/src/main/java/com/submarket/userservice/controller/MainController.java @@ -1,34 +1,43 @@ package com.submarket.userservice.controller; -import com.submarket.userservice.dto.SubDto; import com.submarket.userservice.dto.UserDto; +import com.submarket.userservice.jpa.LikeRepository; import com.submarket.userservice.jpa.SubRepository; import com.submarket.userservice.jpa.UserRepository; -import com.submarket.userservice.jpa.entity.UserEntity; -import com.submarket.userservice.mapper.UserMapper; -import com.submarket.userservice.service.impl.KafkaProducerService; -import com.submarket.userservice.service.impl.MailService; +import com.submarket.userservice.jpa.entity.LikeEntity; +import com.submarket.userservice.service.impl.KafkaProducerServiceImpl; +import com.submarket.userservice.service.impl.MailServiceImpl; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.transaction.Transactional; -import java.util.Optional; @Slf4j @RestController @RequiredArgsConstructor +@Tag(name = "Health API", description = "서버 상태 확인") public class MainController { private final Environment env; - private final MailService mailService; - private final UserRepository userRepository; - private final SubRepository subRepository; - private final KafkaProducerService kafkaProducerService; + @Operation(summary = "상태 확인", description = "인증 정보 없이 접근할 수 있는 EndPoint") + @ApiResponses({ + @ApiResponse(responseCode = "503", description = "Eureka 서버에 미 등록"), + @ApiResponse(responseCode = "401", description = "Token 인증 실패"), + @ApiResponse(responseCode = "403", description = "Spring Security 인증 실패") + + }) @GetMapping("/health") + @Timed(value = "user.health", longTask = true) public String health() { log.info("UserService On"); return env.getProperty("spring.application.name") @@ -37,11 +46,4 @@ public String health() { + ", token secret : " + env.getProperty("token.secret") + ", token expiration time : " + env.getProperty("token.expiration_time"); } - - @GetMapping("/test") - @Transactional - public UserDto test() throws Exception { - - return null; - } } diff --git a/UserService/src/main/java/com/submarket/userservice/controller/SubController.java b/UserService/src/main/java/com/submarket/userservice/controller/SubController.java index 2f9597f..319bd95 100644 --- a/UserService/src/main/java/com/submarket/userservice/controller/SubController.java +++ b/UserService/src/main/java/com/submarket/userservice/controller/SubController.java @@ -3,9 +3,15 @@ import com.submarket.userservice.dto.SubDto; import com.submarket.userservice.jpa.entity.SubEntity; import com.submarket.userservice.mapper.SubMapper; -import com.submarket.userservice.service.impl.SubService; +import com.submarket.userservice.service.SubService; +import com.submarket.userservice.service.impl.SubServiceImpl; import com.submarket.userservice.util.TokenUtil; -import com.submarket.userservice.vo.RequestSub; +import com.submarket.userservice.vo.SubRequest; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; @@ -19,11 +25,17 @@ @RestController @Slf4j @RequiredArgsConstructor +@Tag(name = "구독 API", description = "구독 관련 API") public class SubController { - private final SubService subService; + private final SubService subServiceImpl; private final TokenUtil tokenUtil; - @GetMapping("/sub") + @Operation(summary = "구독중인 상품 전체 조회", description = "사용자가 구독중인 모든 상품 점보를 조회", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 구독 정보 조회 성공") + }) + @GetMapping("/subs") + @Timed(value = "user.sub.findAllSub", longTask = true) public ResponseEntity> findAllSub(@RequestHeader HttpHeaders headers) throws Exception { log.info(this.getClass().getName() + ".findSub Start!"); @@ -34,7 +46,7 @@ public ResponseEntity> findAllSub(@RequestHeader HttpHeaders SubDto subDto = new SubDto(); subDto.setUserId(userId); - List subEntityList = subService.findAllSub(subDto); + List subEntityList = subServiceImpl.findAllSub(subDto); List subDtoList = new ArrayList<>(); @@ -47,17 +59,23 @@ public ResponseEntity> findAllSub(@RequestHeader HttpHeaders return ResponseEntity.ok().body(rMap); - } - @GetMapping("/sub/{subSeq}") + + @Operation(summary = "구독중인 상품 상세 조회", description = "사용자가 가지고 있는 구독 상품 정보 상세 조회", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 구독 정보 조회 성공"), + @ApiResponse(responseCode = "404", description = "Seq 값과 일치하는 구독 정보 없음") + }) + @GetMapping("/subs/{subSeq}") + @Timed(value = "user.sub.findOneSub", longTask = true) public ResponseEntity findOneSub(@PathVariable int subSeq) throws Exception { log.info(this.getClass().getName() + ".findOneSub Start!"); SubDto pDto = new SubDto(); pDto.setSubSeq(subSeq); - SubDto subDto = subService.findOneSub(pDto); + SubDto subDto = subServiceImpl.findOneSub(pDto); if (subDto == null) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); @@ -69,9 +87,16 @@ public ResponseEntity findOneSub(@PathVariable int subSeq) throws Except return ResponseEntity.ok().body(subDto); } - @PostMapping("/sub") + @Operation(summary = "상품 구독 생성", + description = "사용자가 상품 주문 시 구독 생성 및 Kafka 를 통해 Item 수량 감소, Order 정보 생성", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "사용자 구독 성공"), + @ApiResponse(responseCode = "400", description = "중복된 구독 생성") + }) + @PostMapping("/subs") + @Timed(value = "user.sub.createSub", longTask = true) public ResponseEntity createNewSub(@RequestHeader HttpHeaders headers, - @RequestBody SubDto subDto) throws Exception{ + @RequestBody SubDto subDto) throws Exception { log.info(this.getClass().getName() + ".createNewSub Start!"); String userId = tokenUtil.getUserIdByToken(headers); @@ -79,7 +104,7 @@ public ResponseEntity createNewSub(@RequestHeader HttpHeaders headers, subDto.setUserId(userId); - int res = subService.createNewSub(subDto); + int res = subServiceImpl.createNewSub(subDto); if (res == 2) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("중복된 구독"); @@ -94,35 +119,48 @@ public ResponseEntity createNewSub(@RequestHeader HttpHeaders headers, return ResponseEntity.status(HttpStatus.CREATED).body("구독 성공"); } - @PostMapping("/sub/delete") - public String cancelSub(@RequestBody RequestSub requestSub) throws Exception { + @Operation(summary = "상품 구독 취소", + description = "상품 구독 취소 및 Item 수량 복구", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 구독 취소 성공") + }) + @PostMapping("/subs/delete") + @Timed(value = "user.sub.deleteSub", longTask = true) + public ResponseEntity cancelSub(@RequestBody SubRequest subRequest) throws Exception { log.info(this.getClass().getName() + "cancel Sub Start!"); SubDto subDto = new SubDto(); - subDto.setSubSeq(requestSub.getSubSeq()); + subDto.setSubSeq(subRequest.getSubSeq()); - int res = subService.cancelSub(subDto); + int res = subServiceImpl.cancelSub(subDto); log.info(this.getClass().getName() + "cancel Sub End!"); if (res != 1) { - return "구독 취소 실패"; + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("구독 취소 실패"); } - return "구독 취소 성공"; + return ResponseEntity.status(HttpStatus.OK).body("구독 취소 성공"); } - @PostMapping("/sub/update") - public ResponseEntity updateSub(@RequestBody RequestSub requestSub) throws Exception { + + @Operation(summary = "상품 구독 갱신", + description = "상품 구독 갱신 및 주문 생성 (월 단위 자동 호출)", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 구독 갱신 성공") + }) + @PostMapping("/subs/update") + @Timed(value = "user.sub.updateUsb", longTask = true) + public ResponseEntity updateSub(@RequestBody SubRequest subRequest) throws Exception { log.info(this.getClass().getName() + ".updateSub Start!"); SubDto subDto = new SubDto(); - subDto.setSubSeq(requestSub.getSubSeq()); + subDto.setSubSeq(subRequest.getSubSeq()); - int res = subService.updateSub(subDto); + int res = subServiceImpl.updateSub(subDto); if (res != 1) { - return ResponseEntity.ok("갱신 실패"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("갱신 실패"); } log.info(this.getClass().getName() + "updateSub End!"); @@ -131,23 +169,36 @@ public ResponseEntity updateSub(@RequestBody RequestSub requestSub) thro } - @GetMapping("/seller/sub") - public ResponseEntity findSubCount(@RequestBody Map request) throws Exception { + + @Operation(summary = "판매자 상품 Count 조회", + description = "판매중인 상품 총 구독 수 조회", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "판매자 상품 조회 성공") + }) + @GetMapping("/sellers/subs") + @Timed(value = "seller.sub.findSubCount", longTask = true) + public ResponseEntity findSubCount(@RequestBody Map request) throws Exception { // Seller 가 보유하고 있는 상품의 SeqList 를 넘겨주면 총 구독 수를 표시 log.info(this.getClass().getName() + "findSubCount"); List itemSeqList = new LinkedList<>(); itemSeqList = (List) request.get("itemSeqList"); - int count = subService.findSubCount(itemSeqList); + int count = subServiceImpl.findSubCount(itemSeqList); return ResponseEntity.status(HttpStatus.OK).body(count); } - @GetMapping("/seller/sub/{itemSeq}") + @Operation(summary = "판매자 단일 상품 구독 수 조회", + description = "판매중인 상품 별 구독 정보 조회", tags = {"seller"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "판매자 상품 조회 성공") + }) + @GetMapping("/sellers/subs/{itemSeq}") + @Timed(value = "seller.sub.findOneSub", longTask = true) public ResponseEntity findOneSubCount(@PathVariable int itemSeq) throws Exception { log.info(this.getClass().getName() + "findOneSubCount Start!"); - int count = subService.findOneSubCount(itemSeq); + int count = subServiceImpl.findOneSubCount(itemSeq); log.info(this.getClass().getName() + "findOneSubCount End!"); diff --git a/UserService/src/main/java/com/submarket/userservice/controller/UserController.java b/UserService/src/main/java/com/submarket/userservice/controller/UserController.java index ad86f10..d1dbac5 100644 --- a/UserService/src/main/java/com/submarket/userservice/controller/UserController.java +++ b/UserService/src/main/java/com/submarket/userservice/controller/UserController.java @@ -1,52 +1,72 @@ package com.submarket.userservice.controller; +import com.submarket.userservice.dto.ItemDto; import com.submarket.userservice.dto.UserDto; import com.submarket.userservice.mapper.UserMapper; -import com.submarket.userservice.service.impl.MailService; -import com.submarket.userservice.service.impl.UserCheckService; -import com.submarket.userservice.service.impl.UserService; +import com.submarket.userservice.service.UserCheckService; +import com.submarket.userservice.service.UserItemService; +import com.submarket.userservice.service.UserService; +import com.submarket.userservice.service.impl.MailServiceImpl; +import com.submarket.userservice.service.impl.UserCheckServiceImpl; +import com.submarket.userservice.service.impl.UserServiceImpl; import com.submarket.userservice.util.CmmUtil; import com.submarket.userservice.util.TokenUtil; -import com.submarket.userservice.vo.RequestChangePassword; -import com.submarket.userservice.vo.RequestUser; -import com.submarket.userservice.vo.ResponseUser; +import com.submarket.userservice.vo.ItemLikedRequest; +import com.submarket.userservice.vo.ChangePasswordRequest; +import com.submarket.userservice.vo.UserRequest; +import com.submarket.userservice.vo.UserResponse; +import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.catalina.User; -import org.springframework.core.env.Environment; -import org.springframework.data.crossstore.ChangeSetPersister; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @Slf4j @RestController @RequiredArgsConstructor -public class UserController { - private final UserService userService; - private final UserCheckService userCheckService; - private final Environment env; +@Tag(name = "사용자 API", description = "사용자 API") +public class UserController { + private final UserService userServiceImpl; + private final UserCheckService userCheckServiceImpl; private final TokenUtil tokenUtil; - private final MailService mailService; - - /**<---------------------->회원가입*/ + private final MailServiceImpl mailServiceImpl; + + private final UserItemService userItemService; + + /** + * <---------------------->회원가입 + */ + @Operation(summary = "사용자 회원가입", description = "사용자 회원가입 (인증 X)", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "201", description = "사용자 회원가입 성공"), + @ApiResponse(responseCode = "409", description = "사용자 아이디 또는 이메일 중복 발생") + }) @PostMapping("/users") - public ResponseEntity createUser(@RequestBody RequestUser requestUser) throws Exception { + @Timed(value = "user.createUser", longTask = true) + public ResponseEntity createUser(@RequestBody UserRequest userRequest) throws Exception { log.info("--------------> " + this.getClass().getName() + ".createUser Start!"); int res = 0; - if (!userCheckService.checkUserByUserId(requestUser.getUserId())) { + if (!userCheckServiceImpl.checkUserByUserId(userRequest.getUserId())) { // 아이디 중복 return ResponseEntity.status(HttpStatus.CONFLICT).body("중복된 아이디 입니다."); } - UserDto pDTO = UserMapper.INSTANCE.RequestUserToUserDto(requestUser); + UserDto pDTO = UserMapper.INSTANCE.RequestUserToUserDto(userRequest); - res = userService.createUser(pDTO); + res = userServiceImpl.createUser(pDTO); if (res == 0) { /** 아이디 중복 발생 */ return ResponseEntity.status(HttpStatus.CONFLICT).body("중복된 이메일 입니다"); // 충돌 발생 @@ -57,8 +77,13 @@ public ResponseEntity createUser(@RequestBody RequestUser requestUser) t return ResponseEntity.status(HttpStatus.CREATED).body("회원가입 성공"); } - @GetMapping("/user") - public ResponseEntity getUserInfo(@RequestHeader HttpHeaders headers) throws Exception { + @Operation(summary = "사용자 정보 조회", description = "토큰 정보를 사용하여 사용자 정보 조회", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 정보 조회 성공") + }) + @GetMapping("/users") + @Timed(value = "user.getUser", longTask = true) + public ResponseEntity getUserInfo(@RequestHeader HttpHeaders headers) throws Exception { log.info(this.getClass().getName() + "getUserInfo Start!"); // 사용자 토큰을 사용하여 사용자 정보 조회 String userId = tokenUtil.getUserIdByToken(headers); @@ -67,19 +92,26 @@ public ResponseEntity getUserInfo(@RequestHeader HttpHeaders heade return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); } - UserDto userDto = userService.getUserInfoByUserId(userId); + UserDto userDto = userServiceImpl.getUserInfoByUserId(userId); - ResponseUser responseUser = UserMapper.INSTANCE.UserDtoToResponseUser(userDto); + UserResponse userResponse = UserMapper.INSTANCE.UserDtoToResponseUser(userDto); log.info(this.getClass().getName() + "getUserInfo End!"); - return ResponseEntity.ok().body(responseUser); + return ResponseEntity.ok().body(userResponse); } - - /**<------------------------>아이디 중복 확인*/ + @Operation(summary = "사용자 아이디 중복 확인", description = "회원가입 전 사용자 아이디 중복 확인", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용 가능한 아이디"), + @ApiResponse(responseCode = "409", description = "중복된 아이디") + }) + /** + * <------------------------>아이디 중복 확인 + */ @GetMapping("/users/check-id/{userId}") + @Timed(value = "user.check.id", longTask = true) public ResponseEntity checkUserById(@PathVariable String userId) throws Exception { log.info("-------------------- > " + this.getClass().getName() + "checkId Start!"); - boolean checkId = userCheckService.checkUserByUserId(userId); + boolean checkId = userCheckServiceImpl.checkUserByUserId(userId); if (checkId) { log.info("-------------------- > " + this.getClass().getName() + "checkId End!"); @@ -89,11 +121,19 @@ public ResponseEntity checkUserById(@PathVariable String userId) throws return ResponseEntity.status(HttpStatus.CONFLICT).body("아이디 중복 입니다"); } - /**<------------------------>이메일 중복 확인*/ + @Operation(summary = "사용자 이메일 중복 확인", description = "회원가입 전 사용자 이메일 중복 확인", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용 가능한 이메일"), + @ApiResponse(responseCode = "409", description = "중복된 이메일") + }) + /** + * <------------------------>이메일 중복 확인 + */ @GetMapping("/users/check-email/{userEmail}") + @Timed(value = "user.check.email", longTask = true) public ResponseEntity checkUserByEmail(@PathVariable String userEmail) throws Exception { log.info("-------------------- > " + this.getClass().getSimpleName() + "checkEmail Start!"); - boolean checkEmail = userCheckService.checkUserByUserEmail(userEmail); + boolean checkEmail = userCheckServiceImpl.checkUserByUserEmail(userEmail); if (checkEmail) { log.info("-------------------- > " + this.getClass().getName() + "checkEmail End!"); @@ -105,12 +145,20 @@ public ResponseEntity checkUserByEmail(@PathVariable String userEmail) t } - /**<------------------------>아이디 찾기 with UserEmail - * 만약 Email 이 같다면 아이디 정보 일부를 제공*/ + /** + * <------------------------>아이디 찾기 with UserEmail + * 만약 Email 이 같다면 아이디 정보 일부를 제공 + */ + @Operation(summary = "사용자 아이디 찾기", description = "Email을 기반으로 사용자 아이디 찾기", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용 가능한 아이디"), + @ApiResponse(responseCode = "404", description = "사용자 정보를 찾을 수 없음") + }) @GetMapping("/users/find-id/{userEmail}") + @Timed(value = "user.find.id", longTask = true) public ResponseEntity findUserId(@PathVariable String userEmail) throws Exception { log.info("-------------------- > " + this.getClass().getName() + "findUserId Start!"); - UserDto rDTO = userService.getUserInfoByUserEmail(userEmail); + UserDto rDTO = userServiceImpl.getUserInfoByUserEmail(userEmail); String userId; if (rDTO == null) { /** 유저 정보가 없을 경우 Not Found return */ @@ -122,10 +170,18 @@ public ResponseEntity findUserId(@PathVariable String userEmail) throws return ResponseEntity.status(HttpStatus.OK).body(userId); } - /**<------------------------>비밀번호 변경*/ + /** + * <------------------------>비밀번호 변경 + */ + @Operation(summary = "사용자 비밀번호 변경", description = "이전 비밀번호 일치 시 비밀번호 변경", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "비밀번호 변경 성공"), + @ApiResponse(responseCode = "400", description = "이전 비밀번호가 일치하지 않습니다") + }) @PostMapping("/users/changePassword") + @Timed(value = "user.change.password", longTask = true) public ResponseEntity changePassword(@RequestHeader HttpHeaders headers, - @RequestBody RequestChangePassword request) throws Exception { + @RequestBody ChangePasswordRequest request) throws Exception { String userId = tokenUtil.getUserIdByToken(headers); log.info("userId : " + userId); @@ -133,7 +189,7 @@ public ResponseEntity changePassword(@RequestHeader HttpHeaders headers, pDTO.setUserId(userId); pDTO.setUserPassword(request.getOldPassword()); - int check = userService.changeUserPassword(pDTO, request.getNewPassword()); + int check = userServiceImpl.changeUserPassword(pDTO, request.getNewPassword()); if (check == 1) { // 변경 성공 return ResponseEntity.status(HttpStatus.OK).body("비밀번호 변경 성공"); @@ -142,16 +198,22 @@ public ResponseEntity changePassword(@RequestHeader HttpHeaders headers, } - @PostMapping("/user/modify") + @Operation(summary = "사용자 정보 변경", description = "사용자 정보를 변경", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 정보 변경 성공"), + @ApiResponse(responseCode = "400", description = "사용자 정보 변경 실패") + }) + @PostMapping("/users/modify") + @Timed(value = "user.change.userInfo", longTask = true) public ResponseEntity modifyUserInfo(@RequestHeader HttpHeaders headers, @RequestBody UserDto body) - throws Exception { + throws Exception { log.info(this.getClass().getName() + ".modifyUserInfo Start!"); String userId = tokenUtil.getUserIdByToken(headers); log.info("userEmail in Controller : " + body.getUserEmail()); body.setUserId(userId); - int res = userService.modifyUserInfo(body); + int res = userServiceImpl.modifyUserInfo(body); if (res == 1) { return ResponseEntity.ok().body("변경 성공"); } else { @@ -159,22 +221,30 @@ public ResponseEntity modifyUserInfo(@RequestHeader HttpHeaders headers, } } - @PostMapping("/user/fix/find-password") + + @Operation(summary = "사용자 비밀번호 찾기", description = "인증 후 사용자 임시 비밀번호를 Mail로 전송", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Email 로 임시 비밀번호 전송 성공"), + @ApiResponse(responseCode = "400", description = "이메일 정보가 일치하지 않음"), + @ApiResponse(responseCode = "400", description = "아이디로 사용자 정보를 찾을 수 없음") + }) + @PostMapping("/users/fix/find-password") + @Timed(value = "user.find.password", longTask = true) public ResponseEntity findPassword(@RequestBody UserDto userDto) throws Exception { log.info(this.getClass().getName() + ".findPassword Start"); String userId = CmmUtil.nvl(userDto.getUserId()); String userEmail = CmmUtil.nvl(userDto.getUserEmail()); - if (!userCheckService.checkUserByUserId(userId)) { + if (!userCheckServiceImpl.checkUserByUserId(userId)) { // 아이디가 중복 = DB 에 있음 - UserDto checkDto = userService.getUserInfoByUserId(userId); + UserDto checkDto = userServiceImpl.getUserInfoByUserId(userId); if (checkDto.getUserEmail().equals(userEmail)) { // Id로 조회한 Email in DB 값과 넘어온 값이 같으면 Mail 전송 String exPassword = String.valueOf(UUID.randomUUID()); - userService.changeUserPasswordNoAuthorization(userId, exPassword); + userServiceImpl.changeUserPasswordNoAuthorization(userId, exPassword); - mailService.sendMail(userDto.getUserEmail(), "비밀번호 변경", "임시 비밀번호 : " + exPassword + "입니다."); + mailServiceImpl.sendMail(userDto.getUserEmail(), "비밀번호 변경", "임시 비밀번호 : " + exPassword + "입니다."); } else { @@ -189,8 +259,16 @@ public ResponseEntity findPassword(@RequestBody UserDto userDto) throws return ResponseEntity.ok().body(userEmail + "로 임시 비밀번호를 발송 했습니다"); } - @PostMapping("/user/delete") - public ResponseEntity deleteUser(@RequestHeader HttpHeaders headers, @RequestBody RequestUser requestUser) throws Exception { + + @Operation(summary = "사용자 삭제 (비활성화)", description = "인증 후 사용자 정보를 비활성화", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 정보 비활성화 (탈퇴) 성공"), + @ApiResponse(responseCode = "400", description = "이메일 정보가 일치하지 않음"), + @ApiResponse(responseCode = "400", description = "사용자 비밀번호가 일치하지 않음") + }) + @PostMapping("/users/delete") + @Timed(value = "user.deleteUser", longTask = true) + public ResponseEntity deleteUser(@RequestHeader HttpHeaders headers, @RequestBody UserRequest userRequest) throws Exception { /** * 비밀번호가 일치한다면 * 사용자 Status 0으로 변경 @@ -200,13 +278,85 @@ public ResponseEntity deleteUser(@RequestHeader HttpHeaders headers, @Re // GetUserId from Token String userId = tokenUtil.getUserIdByToken(headers); UserDto pDto = new UserDto(); - pDto.setUserPassword(requestUser.getUserPassword()); + pDto.setUserPassword(userRequest.getUserPassword()); pDto.setUserId(userId); - userService.deleteUser(pDto); + userServiceImpl.deleteUser(pDto); log.info(this.getClass().getName() + ".deleteUser Start!"); return ResponseEntity.ok().body("회원탈퇴 완료"); } + + /** + * 좋아요 생성 or 삭제 (이미 있다면 삭제) + * + * @param headers Token + * @param request itemSeq + * @return String 결과 + * @throws Exception + */ + @Operation(summary = "사용자 상품 좋아요", description = "사용자 상품 좋아요", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "상품 좋아요 성공"), + @ApiResponse(responseCode = "200", description = "이미 좋아요를 누른 상태라면, 좋아요 취소"), + @ApiResponse(responseCode = "400", description = "상품 번호와 일치하는 상품 정보가 없음") + }) + @PostMapping("/users/items/liked") + @Timed(value = "user.like", longTask = true) + public ResponseEntity itemLiked(@RequestHeader final HttpHeaders headers, + @RequestBody @Validated final ItemLikedRequest request) throws Exception { + + final String userId = tokenUtil.getUserIdByToken(headers); + + log.debug("userId : " + userId); + + return ResponseEntity.ok().body(userItemService.itemLikedOrDelete(userId, request.getItemSeq())); + } + + @Operation(summary = "사용자 상품 좋아요 확인", description = "사용자 상품 좋아요 유무 확인", tags = {"user"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "사용자 상품 좋아요 유무 확인 성공") + }) + @GetMapping("/users/{userId}/items/{itemSeq}/liked") + @Timed(value = "user.like", longTask = true) + public ResponseEntity isItemLiked(@PathVariable int itemSeq, @PathVariable String userId) throws Exception { + log.info("isItemLiked Start!"); + + log.info("테스트 로그 사용자 상품 좋아요 확인하기"); + final int result = userItemService.likedItemByUserId(userId, itemSeq); + + log.info("result : " + result); + + log.info("isItemLiked Start! "); + + + return ResponseEntity.ok().body(result); + + } + + + @Operation(summary = "좋아요한 상품 목록 조회", description = "사용자가 좋아요한 상품 정보 조회", tags = {"user", "item"}) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "좋아요한 상품 목록 조회 성공") + }) + @GetMapping("/users/items/liked") + public ResponseEntity> findAllLikedItems(@RequestHeader final HttpHeaders headers) throws Exception { + log.info(this.getClass().getName() + ".findAllLikedItems Start!"); + + Map responseBody = new HashMap<>(); + + + List result = userItemService.findAllLikedItems(tokenUtil.getUserIdByToken(headers)); + + responseBody.put("userId", tokenUtil.getUserIdByToken(headers)); + responseBody.put("response", result); + + + log.info(this.getClass().getName() + ".findAllLikedItems End!"); + + + return ResponseEntity.ok().body(responseBody); + } + } diff --git a/UserService/src/main/java/com/submarket/userservice/dto/ItemDto.java b/UserService/src/main/java/com/submarket/userservice/dto/ItemDto.java new file mode 100644 index 0000000..5ff76c3 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/dto/ItemDto.java @@ -0,0 +1,37 @@ +package com.submarket.userservice.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ItemDto { + private int itemSeq; + private String sellerId; + private String itemTitle; + private String itemContents; + private int itemPrice; + private int itemCount; // 상품 수 + private int categorySeq; + private int itemStatus; // 활성화 + private int readCount20; + private int readCount30; + private int readCount40; + private int readCountOther; + private int userAge; + private String mainImagePath; // DB에 저장되어 있는 이미지 정보 + private String subImagePath; + private int isUserLiked; + + + private MultipartFile mainImage; // Front 에서 넘어온 이미지 + private MultipartFile subImage; // Image 2 + +} \ No newline at end of file diff --git a/UserService/src/main/java/com/submarket/userservice/dto/LikeDto.java b/UserService/src/main/java/com/submarket/userservice/dto/LikeDto.java new file mode 100644 index 0000000..2268191 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/dto/LikeDto.java @@ -0,0 +1,21 @@ +package com.submarket.userservice.dto; + +import com.submarket.userservice.jpa.entity.UserEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import net.minidev.json.annotate.JsonIgnore; + +import javax.persistence.*; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LikeDto { + private long likeSeq; + private int itemSeq; + + private String userId; +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/ErrorResponse.java b/UserService/src/main/java/com/submarket/userservice/exception/ErrorResponse.java new file mode 100644 index 0000000..652dbf5 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/ErrorResponse.java @@ -0,0 +1,13 @@ +package com.submarket.userservice.exception; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +@Builder +public class ErrorResponse { + private final String code; + private final String message; +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/ItemException.java b/UserService/src/main/java/com/submarket/userservice/exception/ItemException.java new file mode 100644 index 0000000..7b937e8 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/ItemException.java @@ -0,0 +1,11 @@ +package com.submarket.userservice.exception; + +import com.submarket.userservice.exception.result.ItemExceptionResult; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ItemException extends RuntimeException{ + private final ItemExceptionResult exceptionResult; +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/SubException.java b/UserService/src/main/java/com/submarket/userservice/exception/SubException.java new file mode 100644 index 0000000..ca977cb --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/SubException.java @@ -0,0 +1,13 @@ +package com.submarket.userservice.exception; + +import com.submarket.userservice.exception.result.SubExceptionResult; +import com.submarket.userservice.exception.result.UserExceptionResult; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class SubException extends RuntimeException { + + private final SubExceptionResult exceptionResult; +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/UserException.java b/UserService/src/main/java/com/submarket/userservice/exception/UserException.java new file mode 100644 index 0000000..254e591 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/UserException.java @@ -0,0 +1,12 @@ +package com.submarket.userservice.exception; + +import com.submarket.userservice.exception.result.UserExceptionResult; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class UserException extends RuntimeException { + + private final UserExceptionResult exceptionResult; +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/UserServiceExceptionHandler.java b/UserService/src/main/java/com/submarket/userservice/exception/UserServiceExceptionHandler.java new file mode 100644 index 0000000..13ad59a --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/UserServiceExceptionHandler.java @@ -0,0 +1,51 @@ +package com.submarket.userservice.exception; + +import com.submarket.userservice.exception.result.ItemExceptionResult; +import com.submarket.userservice.exception.result.SubExceptionResult; +import com.submarket.userservice.exception.result.UserExceptionResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +@RestControllerAdvice +@Slf4j +public class UserServiceExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(UserException.class) + public ResponseEntity handleUserException(final UserException exception) { + log.warn("UserException occur : ", exception); + return this.makeErrorResponseEntity(exception.getExceptionResult()); + + } + + @ExceptionHandler(SubException.class) + public ResponseEntity handleSubException(final SubException exception) { + log.warn("SubException occur : ", exception); + return this.makeErrorResponseEntity(exception.getExceptionResult()); + + } + + @ExceptionHandler(ItemException.class) + public ResponseEntity handleItemException(final ItemException exception) { + log.warn("ItemException occur : ", exception); + return this.makeErrorResponseEntity(exception.getExceptionResult()); + + } + + private ResponseEntity makeErrorResponseEntity(final UserExceptionResult exceptionResult) { + return ResponseEntity.status(exceptionResult.getStatus()) + .body(new ErrorResponse(exceptionResult.name(), exceptionResult.getMessage())); + } + + private ResponseEntity makeErrorResponseEntity(final SubExceptionResult exceptionResult) { + return ResponseEntity.status(exceptionResult.getStatus()) + .body(new ErrorResponse(exceptionResult.name(), exceptionResult.getMessage())); + } + + private ResponseEntity makeErrorResponseEntity(final ItemExceptionResult exceptionResult) { + return ResponseEntity.status(exceptionResult.getStatus()) + .body(new ErrorResponse(exceptionResult.name(), exceptionResult.getMessage())); + } +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/result/ItemExceptionResult.java b/UserService/src/main/java/com/submarket/userservice/exception/result/ItemExceptionResult.java new file mode 100644 index 0000000..12083d6 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/result/ItemExceptionResult.java @@ -0,0 +1,14 @@ +package com.submarket.userservice.exception.result; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum ItemExceptionResult { + NOT_MATCHED_ITEM_SEQ(HttpStatus.BAD_REQUEST, "잘못된 상품 번호 입니다"), + ; + private final HttpStatus status; + private final String message; +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/result/SubExceptionResult.java b/UserService/src/main/java/com/submarket/userservice/exception/result/SubExceptionResult.java new file mode 100644 index 0000000..153fe1d --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/result/SubExceptionResult.java @@ -0,0 +1,15 @@ +package com.submarket.userservice.exception.result; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@RequiredArgsConstructor +@Getter +public enum SubExceptionResult { + CAN_NOT_FOUND_SUB_INFO(HttpStatus.NOT_FOUND, "일치하는 구독 정보를 찾을 수 없습니다"), + ; + + private final HttpStatus status; + private final String message; +} diff --git a/UserService/src/main/java/com/submarket/userservice/exception/result/UserExceptionResult.java b/UserService/src/main/java/com/submarket/userservice/exception/result/UserExceptionResult.java new file mode 100644 index 0000000..5ac297a --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/exception/result/UserExceptionResult.java @@ -0,0 +1,18 @@ +package com.submarket.userservice.exception.result; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum UserExceptionResult { + USER_NOT_FOUNT(HttpStatus.NOT_FOUND, "사용자 정보를 찾을 수 없습니다"), + CAN_NOT_CREATE_KAFKA_TOPIC(HttpStatus.INTERNAL_SERVER_ERROR, "Exception in Kafka"), + USER_PASSWORD_NOT_MATCHED(HttpStatus.BAD_REQUEST, "사용자 비밀번호가 일치하지 않습니다"), + ; + + + private final HttpStatus status; + private final String message; +} diff --git a/UserService/src/main/java/com/submarket/userservice/jpa/LikeRepository.java b/UserService/src/main/java/com/submarket/userservice/jpa/LikeRepository.java new file mode 100644 index 0000000..b3c6b5f --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/jpa/LikeRepository.java @@ -0,0 +1,19 @@ +package com.submarket.userservice.jpa; + +import com.submarket.userservice.jpa.entity.LikeEntity; +import com.submarket.userservice.jpa.entity.UserEntity; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface LikeRepository extends CrudRepository { + + + Optional findByUserAndItemSeq(UserEntity user, int itemSeq); + + Optional> findAllByUser(UserEntity user); + +} diff --git a/UserService/src/main/java/com/submarket/userservice/jpa/SubRepository.java b/UserService/src/main/java/com/submarket/userservice/jpa/SubRepository.java index 6153aef..1f87901 100644 --- a/UserService/src/main/java/com/submarket/userservice/jpa/SubRepository.java +++ b/UserService/src/main/java/com/submarket/userservice/jpa/SubRepository.java @@ -12,7 +12,6 @@ import java.util.List; @Repository -@Transactional public interface SubRepository extends CrudRepository { // 구독 갱신 (Count += 1, Date 변경) diff --git a/UserService/src/main/java/com/submarket/userservice/jpa/entity/LikeEntity.java b/UserService/src/main/java/com/submarket/userservice/jpa/entity/LikeEntity.java new file mode 100644 index 0000000..f1c5b2b --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/jpa/entity/LikeEntity.java @@ -0,0 +1,29 @@ +package com.submarket.userservice.jpa.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import lombok.*; +import net.minidev.json.annotate.JsonIgnore; + +import javax.persistence.*; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "likeInfo") +@ToString(exclude = "user") +@Builder +public class LikeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer likeSeq; + + @Column(nullable = false) + private int itemSeq; + + @JsonBackReference + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JsonIgnore + private UserEntity user; +} diff --git a/UserService/src/main/java/com/submarket/userservice/jpa/entity/SubEntity.java b/UserService/src/main/java/com/submarket/userservice/jpa/entity/SubEntity.java index 99fcd78..48d1b88 100644 --- a/UserService/src/main/java/com/submarket/userservice/jpa/entity/SubEntity.java +++ b/UserService/src/main/java/com/submarket/userservice/jpa/entity/SubEntity.java @@ -1,10 +1,8 @@ package com.submarket.userservice.jpa.entity; +import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import net.minidev.json.annotate.JsonIgnore; import javax.persistence.*; @@ -14,7 +12,7 @@ @NoArgsConstructor @Entity @Table(name = "subInfo") -@JsonIgnoreProperties({"user"}) +@ToString(exclude = "user") @Builder public class SubEntity { @@ -31,6 +29,7 @@ public class SubEntity { @Column(nullable = false) private int subCount; + @JsonBackReference @ManyToOne(fetch = FetchType.LAZY, optional = false) @JsonIgnore private UserEntity user; diff --git a/UserService/src/main/java/com/submarket/userservice/jpa/entity/UserEntity.java b/UserService/src/main/java/com/submarket/userservice/jpa/entity/UserEntity.java index 994c05d..650c193 100644 --- a/UserService/src/main/java/com/submarket/userservice/jpa/entity/UserEntity.java +++ b/UserService/src/main/java/com/submarket/userservice/jpa/entity/UserEntity.java @@ -1,5 +1,6 @@ package com.submarket.userservice.jpa.entity; +import com.fasterxml.jackson.annotation.JsonManagedReference; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -7,6 +8,7 @@ import javax.persistence.*; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; @Entity @@ -47,6 +49,11 @@ public class UserEntity { @Column(length = 80) private String userAddress2; + @JsonManagedReference @OneToMany(mappedBy = "user") - private List subEntityList = new ArrayList<>(); + private List subEntityList; + + @JsonManagedReference + @OneToMany(mappedBy = "user") + private List likeEntityList; } diff --git a/UserService/src/main/java/com/submarket/userservice/mapper/SubMapper.java b/UserService/src/main/java/com/submarket/userservice/mapper/SubMapper.java index a73caca..f68e9ea 100644 --- a/UserService/src/main/java/com/submarket/userservice/mapper/SubMapper.java +++ b/UserService/src/main/java/com/submarket/userservice/mapper/SubMapper.java @@ -2,7 +2,6 @@ import com.submarket.userservice.dto.SubDto; import com.submarket.userservice.jpa.entity.SubEntity; -import com.submarket.userservice.vo.ResponseSub; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @@ -10,11 +9,7 @@ @Mapper public interface SubMapper { SubMapper INSTANCE = Mappers.getMapper(SubMapper.class); - - SubEntity subDtoToSubEntity(SubDto subDto); - @Mapping(target = "user", ignore = true) // Entity 를 DTO 에 담을 때 user 정보 무시 SubDto subEntityToSubDto(SubEntity subEntity); - - ResponseSub responseSubToSubDto(SubDto subDto); + SubEntity subDtoToSubEntity(SubDto subDto); } diff --git a/UserService/src/main/java/com/submarket/userservice/mapper/UserMapper.java b/UserService/src/main/java/com/submarket/userservice/mapper/UserMapper.java index b64e83e..1783527 100644 --- a/UserService/src/main/java/com/submarket/userservice/mapper/UserMapper.java +++ b/UserService/src/main/java/com/submarket/userservice/mapper/UserMapper.java @@ -2,8 +2,8 @@ import com.submarket.userservice.dto.UserDto; import com.submarket.userservice.jpa.entity.UserEntity; -import com.submarket.userservice.vo.RequestUser; -import com.submarket.userservice.vo.ResponseUser; +import com.submarket.userservice.vo.UserRequest; +import com.submarket.userservice.vo.UserResponse; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @@ -21,8 +21,8 @@ public interface UserMapper{ UserDto userEntityToUserDto(UserEntity userEntity); - UserDto RequestUserToUserDto(RequestUser requestUser); + UserDto RequestUserToUserDto(UserRequest userRequest); - ResponseUser UserDtoToResponseUser(UserDto userDto); + UserResponse UserDtoToResponseUser(UserDto userDto); } diff --git a/UserService/src/main/java/com/submarket/userservice/security/AuthenticationFilter.java b/UserService/src/main/java/com/submarket/userservice/security/AuthenticationFilter.java index 549eaf8..02418d3 100644 --- a/UserService/src/main/java/com/submarket/userservice/security/AuthenticationFilter.java +++ b/UserService/src/main/java/com/submarket/userservice/security/AuthenticationFilter.java @@ -2,8 +2,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.submarket.userservice.dto.UserDto; -import com.submarket.userservice.service.impl.UserService; -import com.submarket.userservice.vo.RequestLogin; +import com.submarket.userservice.service.UserService; +import com.submarket.userservice.service.impl.UserServiceImpl; +import com.submarket.userservice.vo.LoginRequest; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.extern.slf4j.Slf4j; @@ -29,7 +30,7 @@ public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter { private Environment env; public AuthenticationFilter(AuthenticationManager authenticationManager, - UserService userService, Environment env) { + UserServiceImpl userService, Environment env) { super.setAuthenticationManager(authenticationManager); this.userService = userService; this.env = env; @@ -38,7 +39,7 @@ public AuthenticationFilter(AuthenticationManager authenticationManager, public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { - RequestLogin creds = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class); + LoginRequest creds = new ObjectMapper().readValue(request.getInputStream(), LoginRequest.class); return getAuthenticationManager().authenticate( new UsernamePasswordAuthenticationToken( diff --git a/UserService/src/main/java/com/submarket/userservice/security/WebSecurity.java b/UserService/src/main/java/com/submarket/userservice/security/WebSecurity.java index 3365be9..c8b9331 100644 --- a/UserService/src/main/java/com/submarket/userservice/security/WebSecurity.java +++ b/UserService/src/main/java/com/submarket/userservice/security/WebSecurity.java @@ -1,13 +1,15 @@ package com.submarket.userservice.security; -import com.submarket.userservice.service.impl.UserService; +import com.submarket.userservice.service.impl.UserServiceImpl; import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -15,24 +17,27 @@ @EnableWebSecurity @RequiredArgsConstructor public class WebSecurity extends WebSecurityConfigurerAdapter { - private final UserService userService; + private final UserServiceImpl userServiceImpl; private final BCryptPasswordEncoder bCryptPasswordEncoder; private final Environment env; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); - http.authorizeRequests().antMatchers("/**") - .hasIpAddress(env.getProperty("gateway.ip")) // IP + + http.authorizeRequests() +// .antMatchers("/user-service/**", "/actuator/**").hasIpAddress("20.39.193.121") // IP + .antMatchers("/**").permitAll() .and() .addFilter(getAuthenticationFilter()); // Add Filter http.headers().frameOptions().disable(); } + private AuthenticationFilter getAuthenticationFilter() throws Exception { AuthenticationFilter authenticationFilter = - new AuthenticationFilter(authenticationManager(), userService, env); + new AuthenticationFilter(authenticationManager(), userServiceImpl, env); return authenticationFilter; } @@ -40,6 +45,6 @@ private AuthenticationFilter getAuthenticationFilter() throws Exception { // Check Password @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder); + auth.userDetailsService(userServiceImpl).passwordEncoder(bCryptPasswordEncoder); } } \ No newline at end of file diff --git a/UserService/src/main/java/com/submarket/userservice/service/IKafkaProducerService.java b/UserService/src/main/java/com/submarket/userservice/service/IKafkaProducerService.java deleted file mode 100644 index 35a6699..0000000 --- a/UserService/src/main/java/com/submarket/userservice/service/IKafkaProducerService.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.submarket.userservice.service; - -import com.submarket.userservice.dto.SubDto; - -public interface IKafkaProducerService { - void createNewSub(SubDto subDto) throws Exception; - - void cancelSub(SubDto subDto) throws Exception; -} diff --git a/UserService/src/main/java/com/submarket/userservice/service/KafkaProducerService.java b/UserService/src/main/java/com/submarket/userservice/service/KafkaProducerService.java new file mode 100644 index 0000000..40580e9 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/service/KafkaProducerService.java @@ -0,0 +1,15 @@ +package com.submarket.userservice.service; + +import com.submarket.userservice.dto.LikeDto; +import com.submarket.userservice.dto.SubDto; + +public interface KafkaProducerService { + void createNewSub(SubDto subDto) throws Exception; + + void cancelSub(SubDto subDto) throws Exception; + + + void createItemLike(LikeDto likeDto) throws Exception; + + void cancelItemLike(LikeDto likeDto) throws Exception; +} diff --git a/UserService/src/main/java/com/submarket/userservice/service/ISubCheckService.java b/UserService/src/main/java/com/submarket/userservice/service/SubCheckService.java similarity index 84% rename from UserService/src/main/java/com/submarket/userservice/service/ISubCheckService.java rename to UserService/src/main/java/com/submarket/userservice/service/SubCheckService.java index 6fe70c0..e93e63e 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/ISubCheckService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/SubCheckService.java @@ -1,6 +1,6 @@ package com.submarket.userservice.service; -public interface ISubCheckService { +public interface SubCheckService { boolean SubCheck(Integer subSeq) throws Exception; boolean checkHasSubByItemSeqAndUserId(Integer itemSeq, String userId) throws Exception; diff --git a/UserService/src/main/java/com/submarket/userservice/service/ISubService.java b/UserService/src/main/java/com/submarket/userservice/service/SubService.java similarity index 94% rename from UserService/src/main/java/com/submarket/userservice/service/ISubService.java rename to UserService/src/main/java/com/submarket/userservice/service/SubService.java index b842464..142d4f1 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/ISubService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/SubService.java @@ -5,7 +5,7 @@ import java.util.List; -public interface ISubService { +public interface SubService { List findAllSub(SubDto subDto) throws Exception; SubDto findOneSub(SubDto subDto) throws Exception; diff --git a/UserService/src/main/java/com/submarket/userservice/service/IUserCheckService.java b/UserService/src/main/java/com/submarket/userservice/service/UserCheckService.java similarity index 86% rename from UserService/src/main/java/com/submarket/userservice/service/IUserCheckService.java rename to UserService/src/main/java/com/submarket/userservice/service/UserCheckService.java index 4851fd7..b422485 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/IUserCheckService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/UserCheckService.java @@ -1,6 +1,6 @@ package com.submarket.userservice.service; -public interface IUserCheckService { +public interface UserCheckService { boolean checkUserByUserId(String userId); diff --git a/UserService/src/main/java/com/submarket/userservice/service/UserItemService.java b/UserService/src/main/java/com/submarket/userservice/service/UserItemService.java new file mode 100644 index 0000000..e09a63f --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/service/UserItemService.java @@ -0,0 +1,16 @@ +package com.submarket.userservice.service; + +import com.submarket.userservice.dto.ItemDto; +import com.submarket.userservice.dto.LikeDto; + +import java.util.List; + +public interface UserItemService { + String itemLikedOrDelete(final String userId, final int itemSeq) throws Exception; + + int likedItemByUserId(final String userId, final int itemSeq) throws Exception; + + + List findAllLikedItems(final String userId) throws Exception; + +} diff --git a/UserService/src/main/java/com/submarket/userservice/service/IUserService.java b/UserService/src/main/java/com/submarket/userservice/service/UserService.java similarity index 91% rename from UserService/src/main/java/com/submarket/userservice/service/IUserService.java rename to UserService/src/main/java/com/submarket/userservice/service/UserService.java index c7954ff..ea85e71 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/IUserService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/UserService.java @@ -3,7 +3,7 @@ import com.submarket.userservice.dto.UserDto; import org.springframework.security.core.userdetails.UserDetailsService; -public interface IUserService extends UserDetailsService { +public interface UserService extends UserDetailsService { int createUser(UserDto pDTO) throws Exception; diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/KafkaProducerService.java b/UserService/src/main/java/com/submarket/userservice/service/impl/KafkaProducerService.java deleted file mode 100644 index 3341c33..0000000 --- a/UserService/src/main/java/com/submarket/userservice/service/impl/KafkaProducerService.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.submarket.userservice.service.impl; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.submarket.userservice.dto.SubDto; -import com.submarket.userservice.service.IKafkaProducerService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.kafka.core.KafkaProducerException; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.HashMap; -import java.util.Map; - -@Service -@Slf4j -@RequiredArgsConstructor -public class KafkaProducerService implements IKafkaProducerService { - private final KafkaTemplate kafkaTemplate; - - @Override - public void createNewSub(SubDto subDto) throws Exception { - // 구독 생성 시 Sub Topic 에 구독 정보 전송 --> itemService = 상품 수량 감소, orderService = 새로운 구독 정보 생성 - log.info(this.getClass().getName() + ".createNewSub Start!"); - ObjectMapper mapper = new ObjectMapper(); - String kafkaMessage = ""; - - try { - kafkaMessage = mapper.writeValueAsString(subDto); - } catch (JsonProcessingException ex) { - log.info("JsonProcessingException : " + ex); - ex.printStackTrace(); - } - - kafkaTemplate.send("sub", kafkaMessage); - log.info(this.getClass().getName() + ".createNewSub End!"); - } - - @Override - public void cancelSub(SubDto subDto) throws Exception { - log.info(this.getClass().getName() + ".cancelSub Start!"); - ObjectMapper mapper = new ObjectMapper(); - String kafkaMessage = ""; - - try { - kafkaMessage = mapper.writeValueAsString(subDto); - - } catch (JsonProcessingException exception) { - log.info("JsonProcessingException : " + exception); - exception.printStackTrace(); - - } - - kafkaTemplate.send("sub-cancel", kafkaMessage); - - log.info(this.getClass().getName() + ".cancelSub End!"); - } -} diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/KafkaProducerServiceImpl.java b/UserService/src/main/java/com/submarket/userservice/service/impl/KafkaProducerServiceImpl.java new file mode 100644 index 0000000..2a76a65 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/KafkaProducerServiceImpl.java @@ -0,0 +1,105 @@ +package com.submarket.userservice.service.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.submarket.userservice.dto.LikeDto; +import com.submarket.userservice.dto.SubDto; +import com.submarket.userservice.exception.UserException; +import com.submarket.userservice.exception.result.UserExceptionResult; +import com.submarket.userservice.service.KafkaProducerService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Service; + +import static com.submarket.userservice.constants.KafkaConstants.*; + +@Service +@Slf4j +@RequiredArgsConstructor +public class KafkaProducerServiceImpl implements KafkaProducerService { + private final KafkaTemplate kafkaTemplate; + private final ObjectMapper objectMapper; + + @Override + public void createNewSub(SubDto subDto) throws Exception { + // 구독 생성 시 Sub Topic 에 구독 정보 전송 --> itemService = 상품 수량 감소, orderService = 새로운 구독 정보 생성 + log.debug(this.getClass().getName() + ".createNewSub Start!"); + String kafkaMessage = subDtoToString(subDto); + + kafkaTemplate.send(TOPIC_NAME_SUB, kafkaMessage); + log.info(this.getClass().getName() + ".createNewSub End!"); + } + + @Override + public void cancelSub(SubDto subDto) throws Exception { + log.debug(this.getClass().getName() + ".cancelSub Start!"); + String kafkaMessage = subDtoToString(subDto); + + kafkaTemplate.send(TOPIC_NAME_CANCEL_SUB, kafkaMessage); + + log.info(this.getClass().getName() + ".cancelSub End!"); + } + + private String subDtoToString(final SubDto subDto) throws Exception { + try { + return objectMapper.writeValueAsString(subDto); + + } catch (JsonProcessingException exception) { + log.info("JsonProcessingException : " + exception); + exception.printStackTrace(); + + throw new UserException(UserExceptionResult.CAN_NOT_CREATE_KAFKA_TOPIC); + + } + + } + + + /** + * 상품 좋아요 성공 -> (새로운 좋아요 생성 시) itemService 에 정보 전송 + * + * @param likeDto + * @throws Exception + */ + @Override + public void createItemLike(final LikeDto likeDto) throws Exception { + log.debug(this.getClass().getName() + ".createItemLike Start!"); + String kafkaMessage = likeDtoToString(likeDto); + + kafkaTemplate.send(TOPIC_NAME_ITEM_LIKED, kafkaMessage); + + log.info(this.getClass().getName() + ".createNewSub End!"); + + } + + /** + * 상품 좋아요 취소 -> (이미 좋아요 상태 시) itemService 에 전송 + * + * @param likeDto + * @throws Exception + */ + + @Override + public void cancelItemLike(LikeDto likeDto) throws Exception { + log.debug(this.getClass().getName() + ".cancelItemLike Start!"); + String kafkaMessage = likeDtoToString(likeDto); + + kafkaTemplate.send(TOPIC_NAME_CANCEL_ITEM_LIKED, kafkaMessage); + + log.info(this.getClass().getName() + ".createNewSub End!"); + + } + + + private String likeDtoToString(final LikeDto likeDto) throws Exception { // Dto -> String + try { + return objectMapper.writeValueAsString(likeDto); + } catch (JsonProcessingException ex) { + log.info("JsonProcessingException : " + ex); + ex.printStackTrace(); + throw new UserException(UserExceptionResult.CAN_NOT_CREATE_KAFKA_TOPIC); + } + } + +} diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/MailService.java b/UserService/src/main/java/com/submarket/userservice/service/impl/MailServiceImpl.java similarity index 87% rename from UserService/src/main/java/com/submarket/userservice/service/impl/MailService.java rename to UserService/src/main/java/com/submarket/userservice/service/impl/MailServiceImpl.java index 02bac7e..45a123c 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/impl/MailService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/MailServiceImpl.java @@ -1,9 +1,7 @@ package com.submarket.userservice.service.impl; -import com.submarket.userservice.jpa.SubRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; @@ -13,7 +11,7 @@ @Slf4j @Service(value = "MailService") @RequiredArgsConstructor -public class MailService { +public class MailServiceImpl { private final Environment env; private final JavaMailSender javaMailSender; diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/SchedulerService.java b/UserService/src/main/java/com/submarket/userservice/service/impl/SchedulerServiceImpl.java similarity index 92% rename from UserService/src/main/java/com/submarket/userservice/service/impl/SchedulerService.java rename to UserService/src/main/java/com/submarket/userservice/service/impl/SchedulerServiceImpl.java index 7ecdaa1..1c4e1e2 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/impl/SchedulerService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/SchedulerServiceImpl.java @@ -13,11 +13,11 @@ @Component @Slf4j @RequiredArgsConstructor -public class SchedulerService { +public class SchedulerServiceImpl { /** * 특정 시간에 실행 Batch, and if (day == new Date()) == 결제 + 구독 정보 업데이트 로직 실행 */ - private final SubService subService; + private final SubServiceImpl subServiceImpl; private final SubRepository subRepository; // @Scheduled // 특정 시간에 실행 @@ -40,7 +40,7 @@ public void checkSub() throws Exception { subDto.setSubSeq(subEntity.getSubSeq()); // 구독 업데이트 - subService.updateSub(subDto); + subServiceImpl.updateSub(subDto); }); diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/SubCheckService.java b/UserService/src/main/java/com/submarket/userservice/service/impl/SubCheckServiceImpl.java similarity index 94% rename from UserService/src/main/java/com/submarket/userservice/service/impl/SubCheckService.java rename to UserService/src/main/java/com/submarket/userservice/service/impl/SubCheckServiceImpl.java index 9d6a1e1..8c26597 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/impl/SubCheckService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/SubCheckServiceImpl.java @@ -4,7 +4,6 @@ import com.submarket.userservice.jpa.UserRepository; import com.submarket.userservice.jpa.entity.SubEntity; import com.submarket.userservice.jpa.entity.UserEntity; -import com.submarket.userservice.service.ISubCheckService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -15,7 +14,7 @@ @Service @RequiredArgsConstructor @Slf4j -public class SubCheckService implements ISubCheckService { +public class SubCheckServiceImpl implements com.submarket.userservice.service.SubCheckService { private final SubRepository subRepository; private final UserRepository userRepository; diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/SubService.java b/UserService/src/main/java/com/submarket/userservice/service/impl/SubServiceImpl.java similarity index 85% rename from UserService/src/main/java/com/submarket/userservice/service/impl/SubService.java rename to UserService/src/main/java/com/submarket/userservice/service/impl/SubServiceImpl.java index 2017f87..cf5294c 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/impl/SubService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/SubServiceImpl.java @@ -1,12 +1,13 @@ package com.submarket.userservice.service.impl; import com.submarket.userservice.dto.SubDto; +import com.submarket.userservice.exception.SubException; +import com.submarket.userservice.exception.result.SubExceptionResult; import com.submarket.userservice.jpa.SubRepository; import com.submarket.userservice.jpa.UserRepository; import com.submarket.userservice.jpa.entity.SubEntity; import com.submarket.userservice.jpa.entity.UserEntity; import com.submarket.userservice.mapper.SubMapper; -import com.submarket.userservice.service.ISubService; import com.submarket.userservice.util.CmmUtil; import com.submarket.userservice.util.DateUtil; import lombok.RequiredArgsConstructor; @@ -23,13 +24,13 @@ @Service(value = "SubService") @Slf4j @RequiredArgsConstructor -public class SubService implements ISubService { +public class SubServiceImpl implements com.submarket.userservice.service.SubService { private final SubRepository subRepository; private final UserRepository userRepository; - private final UserService userService; - private final SubCheckService subCheckService; - private final MailService mailService; - private final KafkaProducerService kafkaProducerService; + private final UserServiceImpl userServiceImpl; + private final SubCheckServiceImpl subCheckServiceImpl; + private final MailServiceImpl mailServiceImpl; + private final KafkaProducerServiceImpl kafkaProducerServiceImpl; /** ------------------------- 구독 조회 ------------------------------*/ @Override @@ -55,7 +56,7 @@ public List findAllSub(SubDto subDto) throws RuntimeException{ @Override // 구독 상세 조회 - @Transactional + @Transactional(rollbackOn = Exception.class) public SubDto findOneSub(SubDto subDto) throws Exception { log.info(this.getClass().getName() + "findOne Sub Start!"); int subSeq = subDto.getSubSeq(); @@ -70,7 +71,7 @@ public SubDto findOneSub(SubDto subDto) throws Exception { log.info("subCount : " + subEntity.getSubCount()); if (subEntity == null) { - throw new RuntimeException("SubEntity Null"); + throw new SubException(SubExceptionResult.CAN_NOT_FOUND_SUB_INFO); } pDto = SubMapper.INSTANCE.subEntityToSubDto(subEntity); @@ -96,15 +97,15 @@ public int createNewSub(SubDto subDto) throws Exception{ log.info("itemSeq : " + subDto.getItemSeq()); // 이미 구독 여부 확인 - if (subCheckService.checkHasSubByItemSeqAndUserId(subDto.getItemSeq(), subDto.getUserId())) { + if (subCheckServiceImpl.checkHasSubByItemSeqAndUserId(subDto.getItemSeq(), subDto.getUserId())) { SubEntity subEntity = SubMapper.INSTANCE.subDtoToSubEntity(subDto); log.info("subEntity (itemSeq) : " + subEntity.getItemSeq()); subRepository.save(subEntity); - mailService.sendMail(subDto.getUser().getUserEmail(), "구독 성공", subDto.getUser().getUserName() + "님 구독이 완료 됐습니다!!"); + mailServiceImpl.sendMail(subDto.getUser().getUserEmail(), "구독 성공", subDto.getUser().getUserName() + "님 구독이 완료 됐습니다!!"); // kafka (구독 성공 시 Item Count - 1) - kafkaProducerService.createNewSub(subDto); + kafkaProducerServiceImpl.createNewSub(subDto); res = 1; } else { @@ -136,7 +137,7 @@ public int updateSub(SubDto subDto) { @Transactional public int cancelSub(SubDto subDto) throws Exception{ log.info(this.getClass().getName() + ".cancelSub Start!"); - if (subCheckService.SubCheck(subDto.getSubSeq())) { + if (subCheckServiceImpl.SubCheck(subDto.getSubSeq())) { Optional subEntity = subRepository.findById(subDto.getSubSeq()); subDto.setItemSeq(subEntity.get().getItemSeq()); @@ -144,7 +145,7 @@ public int cancelSub(SubDto subDto) throws Exception{ // not null 삭제 실행 subRepository.deleteById(subDto.getSubSeq()); - kafkaProducerService.cancelSub(subDto); + kafkaProducerServiceImpl.cancelSub(subDto); } else { log.info("구독 정보 찾기 실패"); diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/UserCheckService.java b/UserService/src/main/java/com/submarket/userservice/service/impl/UserCheckServiceImpl.java similarity index 94% rename from UserService/src/main/java/com/submarket/userservice/service/impl/UserCheckService.java rename to UserService/src/main/java/com/submarket/userservice/service/impl/UserCheckServiceImpl.java index 2494bdb..2107abb 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/impl/UserCheckService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/UserCheckServiceImpl.java @@ -2,7 +2,6 @@ import com.submarket.userservice.jpa.UserRepository; import com.submarket.userservice.jpa.entity.UserEntity; -import com.submarket.userservice.service.IUserCheckService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -12,7 +11,7 @@ @Service @Slf4j @RequiredArgsConstructor -public class UserCheckService implements IUserCheckService { +public class UserCheckServiceImpl implements com.submarket.userservice.service.UserCheckService { private final UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/UserItemServiceImpl.java b/UserService/src/main/java/com/submarket/userservice/service/impl/UserItemServiceImpl.java new file mode 100644 index 0000000..df95a63 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/UserItemServiceImpl.java @@ -0,0 +1,152 @@ +package com.submarket.userservice.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.submarket.userservice.client.ItemServiceClient; +import com.submarket.userservice.dto.ItemDto; +import com.submarket.userservice.dto.LikeDto; +import com.submarket.userservice.exception.ItemException; +import com.submarket.userservice.exception.UserException; +import com.submarket.userservice.exception.result.ItemExceptionResult; +import com.submarket.userservice.exception.result.UserExceptionResult; +import com.submarket.userservice.jpa.LikeRepository; +import com.submarket.userservice.jpa.UserRepository; +import com.submarket.userservice.jpa.entity.LikeEntity; +import com.submarket.userservice.jpa.entity.UserEntity; +import com.submarket.userservice.service.KafkaProducerService; +import com.submarket.userservice.service.UserItemService; +import com.submarket.userservice.vo.ItemInfoResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +@Slf4j +@RequiredArgsConstructor +public class UserItemServiceImpl implements UserItemService { + private final LikeRepository likeRepository; + private final UserRepository userRepository; + private final KafkaProducerService kafkaProducerService; + private final ItemServiceClient itemServiceClient; + + private final CircuitBreakerFactory circuitBreakerFactory; + + + /** + * 상품 좋아요 및 좋아요 취소 -> 값이 있다면 delete or create + * + * @param userId + * @param itemSeq + * @return + * @throws Exception + */ + @Override + @Transactional(rollbackOn = Exception.class) + public String itemLikedOrDelete(final String userId, final int itemSeq) throws Exception { + log.debug("item Liked Start!"); + Optional user = Optional.of(userRepository.findByUserId(userId)); + + // 상품 유효성 검사 + log.info("Before call checking item"); + + CircuitBreaker circuitBreaker = circuitBreakerFactory.create("itemCircuit"); + ItemDto itemDto = circuitBreaker.run(() -> itemServiceClient.isItem(itemSeq).getBody(), + throwable -> ItemDto.builder().itemSeq(-1).build()); + + log.info("After call checking item"); + log.info("itemSeq : " + itemDto.getItemSeq()); + + + if (itemDto == null) { + throw new ItemException(ItemExceptionResult.NOT_MATCHED_ITEM_SEQ); + } + + Optional result = likeRepository.findByUserAndItemSeq(user + .orElseThrow(() -> new UserException(UserExceptionResult.USER_NOT_FOUNT)) + , itemSeq); + + + if (result.isPresent()) { // 있을 경우 삭제 + + likeRepository.deleteById(result.get().getLikeSeq()); + + kafkaProducerService.cancelItemLike(LikeDto.builder().itemSeq(itemSeq).userId(userId).build()); + + return "좋아요 취소 성공"; + + } else { // 없을 경우 생성 + + likeRepository.save(LikeEntity.builder() + .user(user.get()) + .itemSeq(itemSeq).build()); + + kafkaProducerService.createItemLike(LikeDto.builder().itemSeq(itemSeq).userId(userId).build()); + + + return "좋아요 성공"; + } + } + + /** + * 상품 좋아요 유무를 확인 + * + * @param userId 사용자 아이디 + * @param itemSeq 상품 번호 + * @return 1, 0 + * @throws Exception + */ + @Override + public int likedItemByUserId(String userId, int itemSeq) throws Exception { + log.info(this.getClass().getName() + ".likedItemByUserId Start!"); + + UserEntity user = userRepository.findByUserId(userId); + + Optional result = likeRepository.findByUserAndItemSeq(user, itemSeq); + + log.info(this.getClass().getName() + ".likedItemByUserId End!"); + // 일치하는 정보가 있을 경우 1 상품 좋아요 유 , Else 0 좋아요 무 + return result.isPresent() ? 1 : 0; + } + + + /** + * 사용자가 좋아요한 상품 목록 조회 + * + * @param userId 사용자 아이디 + * @return 좋아요한 상품 목록 + * @throws Exception + */ + @Override + public List findAllLikedItems(String userId) throws Exception { + log.info("Service Start!"); + + List result = new LinkedList<>(); + UserEntity user = userRepository.findByUserId(userId); + if (user == null) { // 사용자 정보를 찾을 수 없음 + throw new UserException(UserExceptionResult.USER_NOT_FOUNT); + } + + CircuitBreaker circuitBreaker = circuitBreakerFactory.create("findItemInfo"); + Optional> likeEntities = likeRepository.findAllByUser(user); + + if (likeEntities.isPresent()) { // 값이 있다면 상품 정보 조회 시작 + likeEntities.get().forEach(likeEntity -> { + ItemInfoResponse response = circuitBreaker.run(() -> itemServiceClient.findOneItem(likeEntity.getItemSeq()).getBody(), + throwable -> new ItemInfoResponse()); + + result.add(new ObjectMapper().convertValue(response, ItemDto.class)); + }); + } + + log.info("Service End!"); + return result; + } +} diff --git a/UserService/src/main/java/com/submarket/userservice/service/impl/UserService.java b/UserService/src/main/java/com/submarket/userservice/service/impl/UserServiceImpl.java similarity index 87% rename from UserService/src/main/java/com/submarket/userservice/service/impl/UserService.java rename to UserService/src/main/java/com/submarket/userservice/service/impl/UserServiceImpl.java index 23a5f1e..ca68568 100644 --- a/UserService/src/main/java/com/submarket/userservice/service/impl/UserService.java +++ b/UserService/src/main/java/com/submarket/userservice/service/impl/UserServiceImpl.java @@ -1,10 +1,12 @@ package com.submarket.userservice.service.impl; import com.submarket.userservice.dto.UserDto; +import com.submarket.userservice.exception.UserException; +import com.submarket.userservice.exception.result.UserExceptionResult; import com.submarket.userservice.jpa.UserRepository; import com.submarket.userservice.jpa.entity.UserEntity; import com.submarket.userservice.mapper.UserMapper; -import com.submarket.userservice.service.IUserService; +import com.submarket.userservice.service.UserService; import com.submarket.userservice.util.CmmUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,11 +23,12 @@ @Service("UserService") @Slf4j @RequiredArgsConstructor -public class UserService implements IUserService { +public class UserServiceImpl implements UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder passwordEncoder; - private final UserCheckService userCheckService; - private final MailService mailService; + private final UserCheckServiceImpl userCheckServiceImpl; + private final MailServiceImpl mailServiceImpl; + //####################################### 회원가입 #######################################// @Override @@ -33,8 +36,9 @@ public int createUser(UserDto pDTO) throws Exception { log.info("--------------> " + this.getClass().getName() + ".createUser Start !"); /** 아이디 중복 확인 (1 = 중복, 0 = pass)*/ - boolean checkId = userCheckService.checkUserByUserId(pDTO.getUserId()); - boolean checkEmail = userCheckService.checkUserByUserEmail(pDTO.getUserEmail()); + boolean checkId = userCheckServiceImpl.checkUserByUserId(pDTO.getUserId()); + boolean checkEmail = userCheckServiceImpl.checkUserByUserEmail(pDTO.getUserEmail()); + if (checkId && checkEmail) { /** ID or Email 에서 중복확인 완료 실행 가능 */ // 둘 다 0이 넘어와야지만 아래 코드 실행 pDTO.setUserStatus(1); // 사용자 활성화 / (이메일 체크 후 활성화 로직 추가) @@ -43,7 +47,7 @@ public int createUser(UserDto pDTO) throws Exception { userRepository.save(pEntity); // 환영 메일 전송 - mailService.sendMail(pDTO.getUserEmail(), "Welcome!!", pDTO.getUserName() + "님 SubMarket 가입을 환영합니다!"); + mailServiceImpl.sendMail(pDTO.getUserEmail(), "Welcome!!", pDTO.getUserName() + "님 SubMarket 가입을 환영합니다!"); } else { /** 중복 발생 실패 */ @@ -85,7 +89,7 @@ public int changeUserPassword(UserDto pDTO, String newPassword) throws Exception String userPassword = pDTO.getUserPassword(); // 비밀번호가 일치하는지 확인 - boolean checkPassword = userCheckService.isTruePassword(userId, userPassword); + boolean checkPassword = userCheckServiceImpl.isTruePassword(userId, userPassword); // 만약 비밀번호가 일치한다면 if (checkPassword) { @@ -121,11 +125,11 @@ public int changeUserPasswordNoAuthorization(String userId, String newPassword) public int deleteUser(UserDto userDto) throws Exception { log.info(this.getClass().getName() + ".deleteUser Start!"); // 비밀번호 일치 확인 - if (userCheckService.isTruePassword(userDto.getUserId(), userDto.getUserPassword())) { + if (userCheckServiceImpl.isTruePassword(userDto.getUserId(), userDto.getUserPassword())) { // 비밀번호가 일치한다면 userRepository.deleteUserInfo(userDto.getUserId()); } else { - throw new RuntimeException("사용자 비밀번호가 일치하지 않습니다"); + throw new UserException(UserExceptionResult.USER_PASSWORD_NOT_MATCHED); } diff --git a/UserService/src/main/java/com/submarket/userservice/vo/RequestChangePassword.java b/UserService/src/main/java/com/submarket/userservice/vo/ChangePasswordRequest.java similarity index 60% rename from UserService/src/main/java/com/submarket/userservice/vo/RequestChangePassword.java rename to UserService/src/main/java/com/submarket/userservice/vo/ChangePasswordRequest.java index 78b54f9..baff30c 100644 --- a/UserService/src/main/java/com/submarket/userservice/vo/RequestChangePassword.java +++ b/UserService/src/main/java/com/submarket/userservice/vo/ChangePasswordRequest.java @@ -1,9 +1,10 @@ package com.submarket.userservice.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data -public class RequestChangePassword { +public class ChangePasswordRequest { private String oldPassword; private String newPassword; } diff --git a/UserService/src/main/java/com/submarket/userservice/vo/ItemInfoResponse.java b/UserService/src/main/java/com/submarket/userservice/vo/ItemInfoResponse.java new file mode 100644 index 0000000..22cc8d4 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/vo/ItemInfoResponse.java @@ -0,0 +1,34 @@ +package com.submarket.userservice.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ItemInfoResponse { + private int itemSeq; + private String sellerId; + private String itemTitle; + private String itemContents; + private int itemPrice; + private int itemCount; // 상품 수 + private int categorySeq; + private int itemStatus; // 활성화 + private int readCount20; + private int readCount30; + private int readCount40; + private int readCountOther; + private int userAge; + private String mainImagePath; // DB에 저장되어 있는 이미지 정보 + private String subImagePath; + private int isUserLiked; + + + private MultipartFile mainImage; // Front 에서 넘어온 이미지 + private MultipartFile subImage; // Image 2 +} diff --git a/UserService/src/main/java/com/submarket/userservice/vo/ItemLikedRequest.java b/UserService/src/main/java/com/submarket/userservice/vo/ItemLikedRequest.java new file mode 100644 index 0000000..62dd4a1 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/vo/ItemLikedRequest.java @@ -0,0 +1,17 @@ +package com.submarket.userservice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ItemLikedRequest { + private Integer itemSeq; +} diff --git a/UserService/src/main/java/com/submarket/userservice/vo/LoginRequest.java b/UserService/src/main/java/com/submarket/userservice/vo/LoginRequest.java new file mode 100644 index 0000000..8ba3727 --- /dev/null +++ b/UserService/src/main/java/com/submarket/userservice/vo/LoginRequest.java @@ -0,0 +1,17 @@ +package com.submarket.userservice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LoginRequest { + private String userId; + + private String userPassword; +} diff --git a/UserService/src/main/java/com/submarket/userservice/vo/RequestLogin.java b/UserService/src/main/java/com/submarket/userservice/vo/RequestLogin.java deleted file mode 100644 index 154db8b..0000000 --- a/UserService/src/main/java/com/submarket/userservice/vo/RequestLogin.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.submarket.userservice.vo; - -import lombok.Data; - -@Data -public class RequestLogin { - private String userId; - private String userPassword; -} diff --git a/UserService/src/main/java/com/submarket/userservice/vo/RequestSub.java b/UserService/src/main/java/com/submarket/userservice/vo/SubRequest.java similarity index 65% rename from UserService/src/main/java/com/submarket/userservice/vo/RequestSub.java rename to UserService/src/main/java/com/submarket/userservice/vo/SubRequest.java index 77f25d9..c8bf8fd 100644 --- a/UserService/src/main/java/com/submarket/userservice/vo/RequestSub.java +++ b/UserService/src/main/java/com/submarket/userservice/vo/SubRequest.java @@ -1,9 +1,10 @@ package com.submarket.userservice.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data -public class RequestSub { +public class SubRequest { private int itemSeq; diff --git a/UserService/src/main/java/com/submarket/userservice/vo/ResponseSub.java b/UserService/src/main/java/com/submarket/userservice/vo/SubResponse.java similarity index 72% rename from UserService/src/main/java/com/submarket/userservice/vo/ResponseSub.java rename to UserService/src/main/java/com/submarket/userservice/vo/SubResponse.java index 0fa5d23..74eb110 100644 --- a/UserService/src/main/java/com/submarket/userservice/vo/ResponseSub.java +++ b/UserService/src/main/java/com/submarket/userservice/vo/SubResponse.java @@ -1,6 +1,8 @@ package com.submarket.userservice.vo; import com.submarket.userservice.jpa.entity.UserEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -8,7 +10,8 @@ @Data @NoArgsConstructor @AllArgsConstructor -public class ResponseSub { +public class SubResponse { + private Integer subSeq; private int itemSeq; private String subDate; diff --git a/UserService/src/main/java/com/submarket/userservice/vo/ResponseUser.java b/UserService/src/main/java/com/submarket/userservice/vo/UserRequest.java similarity index 88% rename from UserService/src/main/java/com/submarket/userservice/vo/ResponseUser.java rename to UserService/src/main/java/com/submarket/userservice/vo/UserRequest.java index 0845a8a..7ef5c9c 100644 --- a/UserService/src/main/java/com/submarket/userservice/vo/ResponseUser.java +++ b/UserService/src/main/java/com/submarket/userservice/vo/UserRequest.java @@ -10,8 +10,9 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class ResponseUser { +public class UserRequest { private String userId; + private String userPassword; private String userName; private String userEmail; private String userAge; diff --git a/UserService/src/main/java/com/submarket/userservice/vo/RequestUser.java b/UserService/src/main/java/com/submarket/userservice/vo/UserResponse.java similarity index 54% rename from UserService/src/main/java/com/submarket/userservice/vo/RequestUser.java rename to UserService/src/main/java/com/submarket/userservice/vo/UserResponse.java index 1a0a42e..cc4b282 100644 --- a/UserService/src/main/java/com/submarket/userservice/vo/RequestUser.java +++ b/UserService/src/main/java/com/submarket/userservice/vo/UserResponse.java @@ -4,33 +4,15 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotNull; - @Data @AllArgsConstructor @NoArgsConstructor -public class RequestUser { - @NotNull(message = "userId cannot be null") +public class UserResponse { private String userId; - - @NotNull - private String userPassword; - - @NotNull private String userName; - - @Email(message = "이메일이 아닙니다.") private String userEmail; - - @NotNull private String userAge; - - @NotNull private String userPn; - - @NotNull private String userAddress; - private String userAddress2; } diff --git a/UserService/src/test/java/com/submarket/userservice/UserServiceApplicationTests.java b/UserService/src/test/java/com/submarket/userservice/UserServiceImplApplicationTests.java similarity index 82% rename from UserService/src/test/java/com/submarket/userservice/UserServiceApplicationTests.java rename to UserService/src/test/java/com/submarket/userservice/UserServiceImplApplicationTests.java index 6721bb7..d38e686 100644 --- a/UserService/src/test/java/com/submarket/userservice/UserServiceApplicationTests.java +++ b/UserService/src/test/java/com/submarket/userservice/UserServiceImplApplicationTests.java @@ -4,7 +4,7 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class UserServiceApplicationTests { +class UserServiceImplApplicationTests { @Test void contextLoads() {