요구사항: 관리자 입장에서 해당 상품의 주문내역을 조회하고 싶은 상황!
0단계. 요청하는 쪽:item-service , 요청받는 쪽:order-service
// build.gradle 의존성 추가
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
package com.example.itemservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // fegin 클라이언트로 등록
public class ItemServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ItemServiceApplication.class, args);
}
}
package com.example.itemservice.feginClient;
import com.example.itemservice.domain.Order;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
// 발신지: item, 목적지: order
// order service쪽으로 요청할 예정입니다.
// name은 유레카 기준 발신지를 작성, path는 게이트웨이기준,
@FeignClient(name = "ORDER-SERVICE", path = "order-service")
public interface ItemtoOrderFeignClient {
@GetMapping("orders/{productId}/products")
public List<Order> getOrdersByItemId(@PathVariable String productId);
}
1단계,아이템에 더미데이터 넣어주기
실제 Entitiy 역할이 아닌 값만 받아서 처리해주는 용도로 생성
order-service에 있는 order 엔티티와 동일한 필드 값으로 구성하지만 @entitiy를 사용하지 않음
package com.example.itemservice.domain;
import jakarta.persistence.Column;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import java.time.LocalDateTime;
// DTO보다는 VO로 만들기
@Getter @Setter @AllArgsConstructor @NoArgsConstructor
@ToString @Builder
public class Order {
private Long id;
private String orderId;
private Long count;
private LocalDateTime createAt;
private String userId;
private String productId;
}
2단계, 아이템을 이용한 구매내역을 추가해주기
package com.example.orderservice.repository;
import com.example.orderservice.domain.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface OrderRepository extends JpaRepository<Order,Long> {
// feign을 위한 쿼리메서드로 userId를 이용해 해당 유저의 전체 order 목록을 가져오는 메서드
Optional<List<Order>> findOrderByUserIdOrderByCreateAtDesc (String userId);
// productId는 ItemId와 같습니다.
Optional<List<Order>> findOrderByProductId(String productId);
}
package com.example.orderservice.controller;
import com.example.orderservice.domain.Order;
import com.example.orderservice.dto.RequestCreateOrderDto;
import com.example.orderservice.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequiredArgsConstructor
@RequestMapping("order-service")
public class OrderController {
private final OrderService orderService;
@GetMapping("health-check")
public String healthCheck(){
return "item-service server is available";
}
@PostMapping("orders")
public ResponseEntity<?> createOrder(@RequestBody RequestCreateOrderDto requestCreateOrderDto){
orderService.createOrder(requestCreateOrderDto);
return new ResponseEntity(HttpStatus.CREATED);
}
@GetMapping("orders/{userId}/users")
public ResponseEntity<?> getOrderListByUserId(@PathVariable String userId){
List<Order> orderList = orderService.getListOrder(userId)
.orElseThrow(()-> new RuntimeException("없는 유저 아이디로 조회하였습니다"))
.stream().toList(); // .stream().toList()는 Optional에서 유저 아이디로 조회한 주문 목록을 가져와서, 해당 주문 목록을 리스트로 변환하는 과정을 나타냅니다.
return ResponseEntity.ok(orderList);
}
@GetMapping("orders/{productId}/products")
public ResponseEntity<?> getOrderListByProductId(@PathVariable String productId){
List<Order> orderList = orderService.getOrderListByProductId(productId)
.orElseThrow(()-> new RuntimeException("없는 유저 아이디로 조회하셨습니다"))
.stream().toList();
return ResponseEntity.ok(orderList);
}
}
3단계, 아이템을 조회했을 때 해당 아이템과 엮인 구매내역이 함께 조회될 수 있도록 fegin을 이용해 비동기 요청 및 데이터 병합 진행
package com.example.itemservice.dto;
import com.example.itemservice.domain.Item;
import com.example.itemservice.domain.Order;
import lombok.*;
import java.util.List;
// 도메인 Item과 같은 멤버 필드 값에 List<Order>만 추가됨 , @Setter 꼭 필요함
@Getter @Setter @ToString
@AllArgsConstructor @NoArgsConstructor
@Builder
public class ResponseOrderByItemDto {
private String productId; // 아이템 고유 식별 uuid
private String productName; //판매처에서 사용하는 상품명
private Long stock; //재고량
private Long pricePerItem;//개당 가격
private List<Order> orderList;
public static ResponseOrderByItemDto FromDto(Item item){
return ResponseOrderByItemDto.builder()
.productId(item.getProductId())
.productName(item.getProductName())
.stock(item.getStock())
.pricePerItem(item.getPricePerItem())
.build();
}
}
package com.example.itemservice.repository;
import com.example.itemservice.domain.Item;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface ItemRepository extends JpaRepository<Item,Long> {
public Optional<Item> findItemByProductId(String productId);
}
package com.example.itemservice.service;
import com.example.itemservice.domain.Item;
import com.example.itemservice.domain.Order;
import com.example.itemservice.dto.RequestCreateItemDto;
import com.example.itemservice.dto.ResponseOrderByItemDto;
import com.example.itemservice.feginClient.ItemtoOrderFeignClient;
import com.example.itemservice.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
private final ItemtoOrderFeignClient orderFeignClient;
public void createItem(RequestCreateItemDto ItemDto){
Item saveItem = ItemDto.toEntity();
itemRepository.save(saveItem);
}
public ResponseOrderByItemDto findOrderByProduct(String productId){
// 1. 요청측 -> 특정 아이템을 가져옵니다
Item finditem = itemRepository.findItemByProductId(productId)
.orElseThrow(()-> new RuntimeException("존재하지 않는 아이템입니다"));
// 2. 요청하는쪽+요청받는쪽DTO-> ResponseOrderByItemDTO로 변경하는 코드 추가
ResponseOrderByItemDto itemDto = ResponseOrderByItemDto.FromDto(finditem);
// 3. 요청받는쪽으로 FEGIN사용하여 API요청-> feign 클라이언트를 이용해서 특정 아이템의 구매목록을 가져옵니다.
List<Order> orderList = orderFeignClient.getOrdersByItemId(productId);
// 4. 요청받는쪽 값을 요청하는 쪽의 필드로 합침-> ResponseOdrerByItemDTO내 setter를 통해 합치는 과정 진행
itemDto.setOrderList(orderList);
// 5. 합쳐준 DTO(ResponseOrderByItemDto)를 리턴
return itemDto;
}
}
package com.example.itemservice.controller;
import com.example.itemservice.dto.RequestCreateItemDto;
import com.example.itemservice.dto.ResponseOrderByItemDto;
import com.example.itemservice.service.ItemService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
@RequestMapping("item-service")
public class ItemController {
private final ItemService itemService;
@GetMapping("health-check")
public String healthCheck(){
return "item-service service is available";
}
// 등록 성공시 201
@PostMapping("items")
public ResponseEntity<?> CreateItem(@RequestBody RequestCreateItemDto ItemDto){
itemService.createItem(ItemDto);
return new ResponseEntity(HttpStatus.CREATED);
}
// Feign Item to Order
@GetMapping("items/{productId}/orders")
public ResponseEntity<?> getOrdersByProductId(@PathVariable String productId){
ResponseOrderByItemDto dto = itemService.findOrderByProduct(productId);
return ResponseEntity.ok(dto);
}
}
'MSA' 카테고리의 다른 글
개별 마이크로 서비스 Custom Exception 처리 (0) | 2023.11.12 |
---|---|
개별 마이크로 서비스 간 통신 (1) | 2023.11.06 |
Github 레포지토리 config 서버 연동 (0) | 2023.11.01 |
Spring Cloud Config 서버를 활용한 DB 정보관리 (0) | 2023.11.01 |
Spring Cloud Config 서버를 활용한 연동 (1) | 2023.11.01 |