diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index ddd98cb..f77a9ec 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -34,6 +34,7 @@ jobs: MYSQL_DBNAME: ${{ secrets.MYSQL_DBNAME }} MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }} MYSQL_USERNAME: ${{ secrets.MYSQL_USERNAME }} + AUTHORIZATION: ${{ secrets.AUTHORIZATION }} run: ./gradlew build working-directory: ${{ env.working-directory }} - name: build result to slack diff --git a/README.md b/README.md index 6603e97..e5dee12 100644 --- a/README.md +++ b/README.md @@ -1 +1,68 @@ # DoorRush + + +README 이미지_2 + +#### "Restaurnts and more, delivered to your door!" + +#### Door Dash, 배달의 민족을 모티브로 만든 배달 플랫폼 API 서버 프로젝트입니다. + + +

+ +### ![:풍선:](https://a.slack-edge.com/production-standard-emoji-assets/13.0/google-medium/1f388@2x.png) **목표** + +- Door Dash/ 배달의 민족와 같은 배달 앱 서비스를 구현해 내는 것이 목표입니다. +- 유지보수성을 위해 객체지향적 설계와 이론을 바탕으로 클린한 코드를 작성하는 것이 목표입니다. +- 단순한 기능 구현뿐 아니라 대용량 트래픽 처리까지 고려한 기능을 구현하는 것이 목표입니다. +- 테스트를 높은 우선순위를 두어 작성하여 Code Coverage 70%를 유지하고 있습니다. +- CI/CD를 통한 자동화를 구현하여 쉽게 협업이 가능한 프로젝트로 만들고 있습니다. +- Github, Slack을 통한 활발한 소통을 바탕으로 효율적인 협업을 추구합니다. + +

+ +### ![:축구공:](https://a.slack-edge.com/production-standard-emoji-assets/13.0/google-medium/26bd@2x.png) 기술적 이슈와 해결 과정 + +- [외부 API가 응답 코드조차 보낼 수 없는 장애가 발생했다면? Circuit Breaker Pattern로 해결하기](https://github.com/f-lab-edu/DoorRush/issues/67)
+ +- [인증 기능에는 어떤 기술을 사용해야 할까?](https://github.com/ypr821/TIL/blob/main/2022_01/%EC%9D%B8%EC%A6%9D(Authentication)%EA%B3%BC_%EC%9D%B8%EA%B0%80(Authorization).md)
+ +- [인증 관련 보안 이슈](https://github.com/ypr821/TIL/blob/main/2022_01/%EC%9E%90%EB%8F%99%EB%A1%9C%EA%B7%B8%EC%9D%B8%EA%B8%B0%EB%8A%A5_%EB%B3%B4%EC%95%88%EC%9D%B4%EC%8A%88_%EA%B3%A0%EB%AF%BC.md)
+ +- [로그인 체크는 어떻게 하면 좋을까?](https://dev-promise.tistory.com/entry/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%B2%B4%ED%81%AC%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%88%A0%EC%A0%81-%EA%B3%A0%EB%AF%BC) +
+ + + +

+ +### ![:렌치:](https://a.slack-edge.com/production-standard-emoji-assets/13.0/google-medium/1f527@2x.png) **기술 스택** + +### **Java 11, Spring Boot 2.5.6, Spring MVC, MyBatis, gradle, MySQL** + + + + + + +

+ +### ![:책갈피_탭:](https://a.slack-edge.com/production-standard-emoji-assets/13.0/google-large/1f4d1@2x.png)**ERD** + +![image](https://user-images.githubusercontent.com/56250078/152634236-79887af9-5b27-4265-ba99-e737e57c6016.png) + + +

+ +### 주요 기능 시퀀스 다이어그램 + +- 로그인 기능 +![로그인시퀀스다이어그램](https://user-images.githubusercontent.com/56250078/152634301-141280e0-93ec-4b59-87e6-1f5c4f229603.png) + + + + +

+### ![:구급차:](https://a.slack-edge.com/production-standard-emoji-assets/13.0/google-large/1f691@2x.png) 서버 구조도 +- 링크 업데이트 예정 + diff --git a/build.gradle b/build.gradle index e3bb5bc..23bf2af 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ jacoco { dependencies { /* spring-boot-starter-web : Spring MVC 를 사용한 restful 서비스를 개발하는데 사용합니다. */ implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-aop:2.6.2' + implementation 'org.springframework.boot:spring-boot-starter-aop' /* spring-boot-starter-test : JUnit Jupiter, Hamcrest 및 Mockito 를 포함한 라이브러리로 Spring Boot 애플리케이션을 테스트하기 위한 스타터 */ implementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-validation' diff --git a/src/main/java/com/flab/doorrush/domain/authentication/aspect/CheckLoginAspect.java b/src/main/java/com/flab/doorrush/domain/authentication/aspect/CheckLoginAspect.java index 3ede2d0..3826b6e 100644 --- a/src/main/java/com/flab/doorrush/domain/authentication/aspect/CheckLoginAspect.java +++ b/src/main/java/com/flab/doorrush/domain/authentication/aspect/CheckLoginAspect.java @@ -7,6 +7,7 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; @Component @Aspect @@ -18,7 +19,7 @@ public class CheckLoginAspect { @Before("@annotation(com.flab.doorrush.domain.authentication.annotation.CheckLogin)") public void checkLogin() throws AuthenticationCredentialsNotFoundException { String currentId = (String) httpSession.getAttribute(AuthenticationService.LOGIN_SESSION); - if (currentId == null || currentId.equals("null")) { + if (!StringUtils.hasLength(currentId)) { throw new AuthenticationCredentialsNotFoundException("로그인이 필요합니다."); } } diff --git a/src/main/java/com/flab/doorrush/domain/order/api/OrderController.java b/src/main/java/com/flab/doorrush/domain/order/api/OrderController.java new file mode 100644 index 0000000..d222c57 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/api/OrderController.java @@ -0,0 +1,43 @@ +package com.flab.doorrush.domain.order.api; + +import com.flab.doorrush.domain.order.dto.request.MenuDTO; +import com.flab.doorrush.domain.order.dto.request.OrderRequest; +import com.flab.doorrush.domain.order.dto.response.CreateOrderResponse; +import com.flab.doorrush.domain.order.dto.response.OrderMenusCartResponse; +import com.flab.doorrush.domain.order.service.OrderService; +import com.flab.doorrush.global.Response.BasicResponse; +import java.util.List; +import javax.servlet.http.HttpSession; +import javax.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/orders/") +@RequiredArgsConstructor +public class OrderController { + + private final OrderService orderService; + + @GetMapping("check-price") + public ResponseEntity> checkPrice(@RequestBody @Valid List menus) { + return ResponseEntity.status(HttpStatus.OK) + .body(BasicResponse.success(orderService.getTotalPrice(menus))); + } + + @PostMapping + public ResponseEntity> createOrder( + @RequestBody @Valid OrderRequest orderRequest, + HttpSession session) { + CreateOrderResponse response = orderService.createOrder(orderRequest, + session.getAttribute("loginId").toString()); + return ResponseEntity.status(HttpStatus.CREATED).body(BasicResponse.success(response) + ); + } +} diff --git a/src/main/java/com/flab/doorrush/domain/order/dao/OrderMapper.java b/src/main/java/com/flab/doorrush/domain/order/dao/OrderMapper.java new file mode 100644 index 0000000..73782be --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/dao/OrderMapper.java @@ -0,0 +1,22 @@ +package com.flab.doorrush.domain.order.dao; + +import com.flab.doorrush.domain.order.domain.Order; +import com.flab.doorrush.domain.order.domain.OrderMenu; +import com.flab.doorrush.domain.order.dto.request.MenuDTO; +import com.flab.doorrush.domain.order.dto.response.OrderHistory; +import com.flab.doorrush.domain.order.dto.response.OrderMenuPrice; +import java.util.List; +import java.util.Optional; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OrderMapper { + + void insertOrder(Order order); + + void insertOrderMenu(List orderMenus); + + List selectOrderBySeq(Long orderSeq); + + Optional selectPriceByMenuDTO(MenuDTO menuDTO); +} \ No newline at end of file diff --git a/src/main/java/com/flab/doorrush/domain/order/domain/Order.java b/src/main/java/com/flab/doorrush/domain/order/domain/Order.java new file mode 100644 index 0000000..dfefa58 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/domain/Order.java @@ -0,0 +1,20 @@ +package com.flab.doorrush.domain.order.domain; + +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class Order { + + private Long orderSeq; + private Long userSeq; + private String address; + private Long restaurantSeq; + private String restaurantName; + private OrderStatus orderStatus; + private Long amount; + private LocalDateTime orderTime; + +} \ No newline at end of file diff --git a/src/main/java/com/flab/doorrush/domain/order/domain/OrderMenu.java b/src/main/java/com/flab/doorrush/domain/order/domain/OrderMenu.java new file mode 100644 index 0000000..b7a7e9b --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/domain/OrderMenu.java @@ -0,0 +1,16 @@ +package com.flab.doorrush.domain.order.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class OrderMenu { + + private Long menuSeq; + private Long orderSeq; + private int count; + +} \ No newline at end of file diff --git a/src/main/java/com/flab/doorrush/domain/order/domain/OrderStatus.java b/src/main/java/com/flab/doorrush/domain/order/domain/OrderStatus.java new file mode 100644 index 0000000..726403e --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/domain/OrderStatus.java @@ -0,0 +1,6 @@ +package com.flab.doorrush.domain.order.domain; + +public enum OrderStatus { + READY, REQUEST_PAY, APPROVAL_PAY, COMPLETE_PAY, CANCEL ; + +} \ No newline at end of file diff --git a/src/main/java/com/flab/doorrush/domain/order/dto/request/MenuDTO.java b/src/main/java/com/flab/doorrush/domain/order/dto/request/MenuDTO.java new file mode 100644 index 0000000..18aa20d --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/dto/request/MenuDTO.java @@ -0,0 +1,25 @@ +package com.flab.doorrush.domain.order.dto.request; + +import com.flab.doorrush.domain.order.domain.OrderMenu; +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class MenuDTO { + @NotNull + private Long menuSeq; + @NotNull + private int count; + + public OrderMenu toEntity(Long orderSeq){ + return OrderMenu.builder() + .menuSeq(this.menuSeq) + .orderSeq(orderSeq) + .count(this.count) + .build(); + } +} diff --git a/src/main/java/com/flab/doorrush/domain/order/dto/request/OrderRequest.java b/src/main/java/com/flab/doorrush/domain/order/dto/request/OrderRequest.java new file mode 100644 index 0000000..b289a1a --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/dto/request/OrderRequest.java @@ -0,0 +1,22 @@ +package com.flab.doorrush.domain.order.dto.request; + +import java.util.List; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class OrderRequest { + + @NotEmpty + @NotNull + private List menus; + @NotNull + private Long restaurantSeq; + @NotNull + private Long addressSeq; + @NotNull + private Long amount; +} diff --git a/src/main/java/com/flab/doorrush/domain/order/dto/response/CreateOrderResponse.java b/src/main/java/com/flab/doorrush/domain/order/dto/response/CreateOrderResponse.java new file mode 100644 index 0000000..ec8479a --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/dto/response/CreateOrderResponse.java @@ -0,0 +1,18 @@ +package com.flab.doorrush.domain.order.dto.response; + +import com.flab.doorrush.domain.order.domain.Order; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class CreateOrderResponse { + + private Order order; + + public static CreateOrderResponse from(Order order) { + return CreateOrderResponse.builder() + .order(order) + .build(); + } +} diff --git a/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderHistory.java b/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderHistory.java new file mode 100644 index 0000000..f06a805 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderHistory.java @@ -0,0 +1,25 @@ +package com.flab.doorrush.domain.order.dto.response; + +import com.flab.doorrush.domain.order.domain.OrderStatus; +import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class OrderHistory { + + private Long orderSeq; + private String address; + private Long restaurantSeq; + private String restaurantName; + private Long amount; + private OrderStatus orderStatus; + private String menuName; + private String menuPrice; + private String menuCount; + private LocalDateTime orderTime; + +} \ No newline at end of file diff --git a/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderMenuPrice.java b/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderMenuPrice.java new file mode 100644 index 0000000..906c973 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderMenuPrice.java @@ -0,0 +1,14 @@ +package com.flab.doorrush.domain.order.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class OrderMenuPrice { + + private String name; + private Long price; + private Long menuSumPrice; + +} \ No newline at end of file diff --git a/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderMenusCartResponse.java b/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderMenusCartResponse.java new file mode 100644 index 0000000..f7fd8c0 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/dto/response/OrderMenusCartResponse.java @@ -0,0 +1,12 @@ +package com.flab.doorrush.domain.order.dto.response; + +import java.util.List; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class OrderMenusCartResponse { + private Long totalPrice; + private List orderMenuCarts; +} diff --git a/src/main/java/com/flab/doorrush/domain/order/exception/OrderException.java b/src/main/java/com/flab/doorrush/domain/order/exception/OrderException.java new file mode 100644 index 0000000..271a3a8 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/exception/OrderException.java @@ -0,0 +1,9 @@ +package com.flab.doorrush.domain.order.exception; + +public class OrderException extends RuntimeException { + + public OrderException(String message) { + super(message); + } + +} diff --git a/src/main/java/com/flab/doorrush/domain/order/service/OrderService.java b/src/main/java/com/flab/doorrush/domain/order/service/OrderService.java new file mode 100644 index 0000000..9915e56 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/order/service/OrderService.java @@ -0,0 +1,93 @@ +package com.flab.doorrush.domain.order.service; + +import com.flab.doorrush.domain.order.dao.OrderMapper; +import com.flab.doorrush.domain.order.domain.Order; +import com.flab.doorrush.domain.order.domain.OrderMenu; +import com.flab.doorrush.domain.order.domain.OrderStatus; +import com.flab.doorrush.domain.order.dto.request.MenuDTO; +import com.flab.doorrush.domain.order.dto.request.OrderRequest; +import com.flab.doorrush.domain.order.dto.response.CreateOrderResponse; +import com.flab.doorrush.domain.order.dto.response.OrderHistory; +import com.flab.doorrush.domain.order.dto.response.OrderMenuPrice; +import com.flab.doorrush.domain.order.dto.response.OrderMenusCartResponse; +import com.flab.doorrush.domain.order.exception.OrderException; +import com.flab.doorrush.domain.restaurant.dao.RestaurantMapper; +import com.flab.doorrush.domain.restaurant.domain.Restaurant; +import com.flab.doorrush.domain.user.dao.UserMapper; +import com.flab.doorrush.domain.user.domain.User; +import com.flab.doorrush.domain.user.exception.UserNotFoundException; +import com.flab.doorrush.domain.user.service.UserAddressService; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Slf4j +public class OrderService { + + private final OrderMapper orderMapper; + private final UserMapper userMapper; + private final RestaurantMapper restaurantMapper; + private final UserAddressService userAddressService; + + @Transactional + public CreateOrderResponse createOrder(OrderRequest orderRequest, String loginId) { + User user = userMapper.selectUserById(loginId) + .orElseThrow(() -> new UserNotFoundException("회원정보가 없습니다.")); + + if (orderRequest.getMenus().isEmpty()) { + throw new OrderException("메뉴 정보를 입력해주세요."); + } + + Restaurant restaurant = restaurantMapper.selectRestaurantBySeq(orderRequest.getRestaurantSeq()); + + Order order = Order.builder() + .userSeq(user.getUserSeq()) + .address(userAddressService.getOriginAddress(orderRequest.getAddressSeq()).getOriginAddress()) + .restaurantSeq(orderRequest.getRestaurantSeq()) + .restaurantName(restaurant.getRestaurantName()) + .orderStatus(OrderStatus.READY) + .amount(orderRequest.getAmount()) + .build(); + + orderMapper.insertOrder(order); + + if (order.getOrderSeq() == null) { + throw new OrderException("주문이 정상적으로 처리되지 않았습니다. 다시 시도해주세요"); + } + + addOrderMenu(orderRequest.getMenus(), order.getOrderSeq()); + return CreateOrderResponse.from(order); + } + + private void addOrderMenu(List menus, Long orderSeq) { + List orderMenus = new ArrayList<>(); + menus.forEach(menu -> orderMenus.add(menu.toEntity(orderSeq))); + orderMapper.insertOrderMenu(orderMenus); + List list = orderMapper.selectOrderBySeq(orderSeq); + if (list.isEmpty()) { + throw new OrderException("addOrderMenu 실행 중 오류가 발생했습니다."); + } + } + + public OrderMenusCartResponse getTotalPrice(List menus) { + List orderMenuCarts = new ArrayList<>(); + menus.forEach(menu -> orderMenuCarts.add(orderMapper.selectPriceByMenuDTO(menu) + .orElseThrow(() -> new OrderException("메뉴 정보를 확인해주세요.")))); + + Long totalPrice = orderMenuCarts.stream() + .map(OrderMenuPrice::getMenuSumPrice) + .mapToLong(Math::toIntExact) + .sum(); + + return OrderMenusCartResponse.builder() + .orderMenuCarts(orderMenuCarts) + .totalPrice(totalPrice) + .build(); + } + +} diff --git a/src/main/java/com/flab/doorrush/domain/restaurant/api/RestaurantController.java b/src/main/java/com/flab/doorrush/domain/restaurant/api/RestaurantController.java index da318c8..fc461d4 100644 --- a/src/main/java/com/flab/doorrush/domain/restaurant/api/RestaurantController.java +++ b/src/main/java/com/flab/doorrush/domain/restaurant/api/RestaurantController.java @@ -20,11 +20,11 @@ public class RestaurantController { private final RestaurantService restaurantService; + @PostMapping("/{ownerSeq}/addRestaurant") public ResponseEntity> addRestaurant(@PathVariable Long ownerSeq, @Valid @RequestBody AddRestaurantRequest addRestaurantRequest) { - addRestaurantRequest.setOwnerSeq(ownerSeq); - restaurantService.addRestaurant(addRestaurantRequest); + restaurantService.addRestaurant(addRestaurantRequest, ownerSeq); return ResponseEntity.status(HttpStatus.OK).build(); } diff --git a/src/main/java/com/flab/doorrush/domain/restaurant/dao/RestaurantMapper.java b/src/main/java/com/flab/doorrush/domain/restaurant/dao/RestaurantMapper.java index 2006701..894f74b 100644 --- a/src/main/java/com/flab/doorrush/domain/restaurant/dao/RestaurantMapper.java +++ b/src/main/java/com/flab/doorrush/domain/restaurant/dao/RestaurantMapper.java @@ -6,6 +6,8 @@ @Mapper public interface RestaurantMapper { + Restaurant selectRestaurantBySeq(long restaurantSeq); + int insertRestaurant(Restaurant restaurant); Restaurant selectRestaurantByRestaurantSeq(Long restaurantSeq); diff --git a/src/main/java/com/flab/doorrush/domain/restaurant/domain/Restaurant.java b/src/main/java/com/flab/doorrush/domain/restaurant/domain/Restaurant.java index f68f4ea..59770ba 100644 --- a/src/main/java/com/flab/doorrush/domain/restaurant/domain/Restaurant.java +++ b/src/main/java/com/flab/doorrush/domain/restaurant/domain/Restaurant.java @@ -1,5 +1,6 @@ package com.flab.doorrush.domain.restaurant.domain; +import com.flab.doorrush.domain.user.domain.YnStatus; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -14,7 +15,7 @@ public class Restaurant { private Long restaurantSeq; private Long ownerSeq; private String category; - private char openYN; + private YnStatus openYn; private String restaurantName; private String introduction; private Long minimumOrderAmount; diff --git a/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/AddRestaurantRequest.java b/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/AddRestaurantRequest.java index cfdb8b6..9fffe85 100644 --- a/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/AddRestaurantRequest.java +++ b/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/AddRestaurantRequest.java @@ -1,27 +1,25 @@ package com.flab.doorrush.domain.restaurant.dto.request; import com.flab.doorrush.domain.restaurant.domain.Restaurant; +import com.flab.doorrush.domain.user.domain.YnStatus; import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import lombok.Setter; @Getter @Builder -@Setter @AllArgsConstructor public class AddRestaurantRequest { @NotNull private RestaurantAddressRequest restaurantAddressRequest; - private Long ownerSeq; - @NotNull private String category; - private char openYN; + @NotNull + private YnStatus openYn; @NotNull private String restaurantName; @@ -29,17 +27,18 @@ public class AddRestaurantRequest { @NotNull private String introduction; + @NotNull private Long minimumOrderAmount; - public Restaurant toEntity(Long addressSeq) { + public Restaurant toEntity(Long addressSeq, Long ownerSeq) { return Restaurant.builder() - .ownerSeq(this.ownerSeq) + .ownerSeq(ownerSeq) .category(this.category) - .openYN('N') + .openYn(this.openYn) .restaurantName(this.restaurantName) .introduction(this.introduction) - .minimumOrderAmount(0L) + .minimumOrderAmount(this.minimumOrderAmount) .addressSeq(addressSeq) .build(); } diff --git a/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/RestaurantAddressRequest.java b/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/RestaurantAddressRequest.java index 20f0dd7..d1b675f 100644 --- a/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/RestaurantAddressRequest.java +++ b/src/main/java/com/flab/doorrush/domain/restaurant/dto/request/RestaurantAddressRequest.java @@ -4,11 +4,9 @@ import javax.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; -import lombok.Setter; @Getter @Builder -@Setter public class RestaurantAddressRequest { @NotNull diff --git a/src/main/java/com/flab/doorrush/domain/restaurant/exception/AddRestaurantException.java b/src/main/java/com/flab/doorrush/domain/restaurant/exception/AddRestaurantException.java index ab42cd1..3d10269 100644 --- a/src/main/java/com/flab/doorrush/domain/restaurant/exception/AddRestaurantException.java +++ b/src/main/java/com/flab/doorrush/domain/restaurant/exception/AddRestaurantException.java @@ -1,7 +1,6 @@ package com.flab.doorrush.domain.restaurant.exception; -public class AddRestaurantException extends - RuntimeException { +public class AddRestaurantException extends RuntimeException { public AddRestaurantException(Exception e, String message) { } diff --git a/src/main/java/com/flab/doorrush/domain/restaurant/service/RestaurantService.java b/src/main/java/com/flab/doorrush/domain/restaurant/service/RestaurantService.java index e64d1c7..1c65e31 100644 --- a/src/main/java/com/flab/doorrush/domain/restaurant/service/RestaurantService.java +++ b/src/main/java/com/flab/doorrush/domain/restaurant/service/RestaurantService.java @@ -19,12 +19,12 @@ public class RestaurantService { @Transactional - public void addRestaurant(AddRestaurantRequest addRestaurantRequest) { + public void addRestaurant(AddRestaurantRequest addRestaurantRequest, Long ownerSeq) { Address address = addRestaurantRequest.getRestaurantAddressRequest().toEntity(); try { userAddressMapper.insertAddress(address); Long addressSeq = userAddressMapper.selectAddressSeq(address); - Restaurant restaurant = addRestaurantRequest.toEntity(addressSeq); + Restaurant restaurant = addRestaurantRequest.toEntity(addressSeq, ownerSeq); restaurantMapper.insertRestaurant(restaurant); } catch (Exception e) { throw new AddRestaurantException(e, "식당 insert 처리 중 예외 발생"); diff --git a/src/main/java/com/flab/doorrush/domain/user/dao/UserAddressMapper.java b/src/main/java/com/flab/doorrush/domain/user/dao/UserAddressMapper.java index d2b1ee3..b055164 100644 --- a/src/main/java/com/flab/doorrush/domain/user/dao/UserAddressMapper.java +++ b/src/main/java/com/flab/doorrush/domain/user/dao/UserAddressMapper.java @@ -3,6 +3,7 @@ import com.flab.doorrush.domain.user.domain.Address; import com.flab.doorrush.domain.user.domain.UserAddress; import java.util.List; +import java.util.Optional; import org.apache.ibatis.annotations.Mapper; @Mapper @@ -22,5 +23,7 @@ public interface UserAddressMapper { boolean isExistsAddress(Long addressSeq); + Optional
selectAddressBySeq(Long addressSeq); + Long selectAddressSeq(Address address); } diff --git a/src/main/java/com/flab/doorrush/domain/user/dto/response/AddressInfoDTO.java b/src/main/java/com/flab/doorrush/domain/user/dto/response/AddressInfoDTO.java new file mode 100644 index 0000000..d320b54 --- /dev/null +++ b/src/main/java/com/flab/doorrush/domain/user/dto/response/AddressInfoDTO.java @@ -0,0 +1,12 @@ +package com.flab.doorrush.domain.user.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class AddressInfoDTO { + private String roadAddress; + private String buildingName; + private String originAddress; +} diff --git a/src/main/java/com/flab/doorrush/domain/user/service/UserAddressService.java b/src/main/java/com/flab/doorrush/domain/user/service/UserAddressService.java index 397541c..4edda9c 100644 --- a/src/main/java/com/flab/doorrush/domain/user/service/UserAddressService.java +++ b/src/main/java/com/flab/doorrush/domain/user/service/UserAddressService.java @@ -5,10 +5,16 @@ import com.flab.doorrush.domain.user.domain.Address; import com.flab.doorrush.domain.user.domain.UserAddress; import com.flab.doorrush.domain.user.dto.request.UserAddressRequest; +import com.flab.doorrush.domain.user.dto.response.AddressInfoDTO; import com.flab.doorrush.domain.user.dto.response.UserAddressResponse; import com.flab.doorrush.domain.user.exception.NotExistsAddressException; +import com.flab.doorrush.global.api.KakaoAddressApi; +import com.flab.doorrush.global.dto.request.KakaoApiGetAddressRequest; +import com.flab.doorrush.global.dto.response.kakao.KakaoApiGetAddressResponse; +import com.flab.doorrush.global.exception.KakaoApiResponseException; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,9 +23,11 @@ public class UserAddressService { private final UserAddressMapper userAddressMapper; + private final KakaoAddressApi kakaoAddressApi; public List getUserAddress(Long userSeq) { - return UserAddressResponse.fromUserAddressResponse(userAddressMapper.selectUserAddressAll(userSeq)); + return UserAddressResponse.fromUserAddressResponse( + userAddressMapper.selectUserAddressAll(userSeq)); } @Transactional @@ -57,4 +65,39 @@ private boolean shouldUpdateUserAddress(Long userSeq, UserAddressRequest userAdd && userAddressRequest.isDefault(userAddressRequest.getDefaultYn()); } + public AddressInfoDTO getOriginAddress(Long addressSeq) { + Address address = userAddressMapper.selectAddressBySeq(addressSeq) + .orElseThrow(() -> new NotExistsAddressException("주소정보가 잘못되었습니다.")); + + KakaoApiGetAddressRequest request = KakaoApiGetAddressRequest.builder() + .x(address.getSpotX().toString()) + .y(address.getSpotY().toString()) + .build(); + return parseKakaoApiGetAddressResponse(kakaoAddressApi.getAddressBySpot(request)); + } + + private AddressInfoDTO parseKakaoApiGetAddressResponse( + ResponseEntity kakaoApiGetAddressResponse) { + KakaoApiGetAddressResponse response = kakaoApiGetAddressResponse.getBody(); + + if (!response.getMeta().isExist()) { + throw new KakaoApiResponseException("API 응답결과가 없습니다."); + } + + String roadAddress = ""; + String buildingName = ""; + String originAddress = ""; + if (!response.getMainAddress().getRoadAddress().isExist()) { + roadAddress = response.getMainAddress().getRoadAddress().getAddressName(); + buildingName = response.getMainAddress().getRoadAddress().getBuildingName(); + } + if (!response.getMainAddress().getAddress().isExist()) { + originAddress = response.getMainAddress().getAddress().getAddressName(); + } + return AddressInfoDTO.builder() + .roadAddress(roadAddress) + .originAddress(originAddress) + .buildingName(buildingName) + .build(); + } } diff --git a/src/main/java/com/flab/doorrush/global/api/KakaoAddressApi.java b/src/main/java/com/flab/doorrush/global/api/KakaoAddressApi.java new file mode 100644 index 0000000..7ff2eb5 --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/api/KakaoAddressApi.java @@ -0,0 +1,51 @@ +package com.flab.doorrush.global.api; + +import com.flab.doorrush.global.dto.request.KakaoApiGetAddressRequest; +import com.flab.doorrush.global.dto.response.kakao.KakaoApiGetAddressResponse; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +@Component +@RequiredArgsConstructor +public class KakaoAddressApi { + + private final RestTemplate restTemplate; + + @Value("${api.authorization}") + private String AUTHORIZATION; + + public static final String KAKAO_HEADER = "KakaoAK "; + public static final String KAKAO_HOST = "https://dapi.kakao.com"; + public static final String KAKAO_URL = "/v2/local/geo/coord2address.json"; + + public ResponseEntity getAddressBySpot( + KakaoApiGetAddressRequest getAddressRequest) { + + URI url = UriComponentsBuilder.fromHttpUrl(KAKAO_HOST + KAKAO_URL) + .queryParam("x", getAddressRequest.getX()) + .queryParam("y", getAddressRequest.getY()) + .build() + .toUri(); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.AUTHORIZATION, KAKAO_HEADER + AUTHORIZATION); + headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); + headers.add(HttpHeaders.CONTENT_TYPE, String.valueOf(MediaType.APPLICATION_JSON)); + headers.add(HttpHeaders.ACCEPT_CHARSET, String.valueOf(StandardCharsets.UTF_8)); + + HttpEntity entity = new HttpEntity<>(headers); + + return restTemplate.exchange(url, HttpMethod.GET, entity, KakaoApiGetAddressResponse.class); + } + +} diff --git a/src/main/java/com/flab/doorrush/global/config/HttpConfig.java b/src/main/java/com/flab/doorrush/global/config/HttpConfig.java new file mode 100644 index 0000000..84b5448 --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/config/HttpConfig.java @@ -0,0 +1,15 @@ +package com.flab.doorrush.global.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class HttpConfig { + + @Bean + public RestTemplate RestTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } +} diff --git a/src/main/java/com/flab/doorrush/global/dto/request/KakaoApiGetAddressRequest.java b/src/main/java/com/flab/doorrush/global/dto/request/KakaoApiGetAddressRequest.java new file mode 100644 index 0000000..cdf2063 --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/dto/request/KakaoApiGetAddressRequest.java @@ -0,0 +1,11 @@ +package com.flab.doorrush.global.dto.request; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class KakaoApiGetAddressRequest { + private String x; + private String y; +} diff --git a/src/main/java/com/flab/doorrush/global/dto/response/kakao/Address.java b/src/main/java/com/flab/doorrush/global/dto/response/kakao/Address.java new file mode 100644 index 0000000..d697d99 --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/dto/response/kakao/Address.java @@ -0,0 +1,36 @@ +package com.flab.doorrush.global.dto.response.kakao; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public class Address { + + @JsonProperty("address_name") + private String addressName; + + @JsonProperty("region_1depth_name") + private String region1DepthName; + + @JsonProperty("region_2depth_name") + private String region2DepthName; + + @JsonProperty("region_3depth_name") + private String region3DepthName; + + @JsonProperty("mountain_yn") + private String mountainYn; + + @JsonProperty("main_address_no") + private String mainAddressNo; + + @JsonProperty("sub_address_no") + private String subAddressNo; + + @JsonProperty("zip_code") + private String zipCode; + + public boolean isExist() { + return addressName.isEmpty(); + } +} diff --git a/src/main/java/com/flab/doorrush/global/dto/response/kakao/GetAddressInfo.java b/src/main/java/com/flab/doorrush/global/dto/response/kakao/GetAddressInfo.java new file mode 100644 index 0000000..644ac4a --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/dto/response/kakao/GetAddressInfo.java @@ -0,0 +1,14 @@ +package com.flab.doorrush.global.dto.response.kakao; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public class GetAddressInfo { + + @JsonProperty("road_address") + private RoadAddress roadAddress; + + private Address address; + +} diff --git a/src/main/java/com/flab/doorrush/global/dto/response/kakao/KakaoApiGetAddressResponse.java b/src/main/java/com/flab/doorrush/global/dto/response/kakao/KakaoApiGetAddressResponse.java new file mode 100644 index 0000000..010e3c1 --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/dto/response/kakao/KakaoApiGetAddressResponse.java @@ -0,0 +1,16 @@ +package com.flab.doorrush.global.dto.response.kakao; + +import java.util.List; +import lombok.Getter; + +@Getter +public class KakaoApiGetAddressResponse { + + private Meta meta; + private List documents; + + public GetAddressInfo getMainAddress() { + return documents.get(0); + } + +} diff --git a/src/main/java/com/flab/doorrush/global/dto/response/kakao/Meta.java b/src/main/java/com/flab/doorrush/global/dto/response/kakao/Meta.java new file mode 100644 index 0000000..1bb7b9d --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/dto/response/kakao/Meta.java @@ -0,0 +1,13 @@ +package com.flab.doorrush.global.dto.response.kakao; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Meta { + + @JsonProperty("total_count") + private String totalCount; + + public boolean isExist() { + return !totalCount.equals("0"); + } +} diff --git a/src/main/java/com/flab/doorrush/global/dto/response/kakao/RoadAddress.java b/src/main/java/com/flab/doorrush/global/dto/response/kakao/RoadAddress.java new file mode 100644 index 0000000..fb5bd5c --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/dto/response/kakao/RoadAddress.java @@ -0,0 +1,42 @@ +package com.flab.doorrush.global.dto.response.kakao; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public class RoadAddress { + + @JsonProperty("address_name") + private String addressName; + + @JsonProperty("region_1depth_name") + private String region1DepthName; + + @JsonProperty("region_2depth_name") + private String region2DepthName; + + @JsonProperty("region_3depth_name") + private String region3DepthName; + + @JsonProperty("road_name") + private String roadName; + + @JsonProperty("underground_yn") + private String undergroundYn; + + @JsonProperty("main_building_no") + private String mainBuildingNo; + + @JsonProperty("sub_building_no") + private String subBuildingNo; + + @JsonProperty("building_name") + private String buildingName; + + @JsonProperty("zone_no") + private String zoneNo; + + public boolean isExist() { + return addressName.isEmpty(); + } +} diff --git a/src/main/java/com/flab/doorrush/global/exception/GlobalExceptionHandler.java b/src/main/java/com/flab/doorrush/global/exception/GlobalExceptionHandler.java index e72b356..0edf971 100644 --- a/src/main/java/com/flab/doorrush/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/flab/doorrush/global/exception/GlobalExceptionHandler.java @@ -5,6 +5,7 @@ import com.flab.doorrush.domain.authentication.exception.InvalidPasswordException; import com.flab.doorrush.domain.authentication.exception.SessionAuthenticationException; import com.flab.doorrush.domain.authentication.exception.SessionLoginIdNotFoundException; +import com.flab.doorrush.domain.order.exception.OrderException; import com.flab.doorrush.domain.restaurant.exception.AddRestaurantException; import com.flab.doorrush.global.Response.BasicResponse; import com.flab.doorrush.domain.user.exception.DuplicatedUserIdException; @@ -72,12 +73,26 @@ public ResponseEntity> methodArgumentNotValidException( } @ExceptionHandler(NotExistsAddressException.class) - public ResponseEntity> NotExistsAddressException( + public ResponseEntity> methodArgumentNotValidException( NotExistsAddressException e) { log.error(e.getMessage(), e); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(BasicResponse.fail(e.getMessage())); } + @ExceptionHandler(KakaoApiResponseException.class) + public ResponseEntity> KakaoApiResponseException( + KakaoApiResponseException e) { + log.error(e.getMessage(), e); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(BasicResponse.fail(e.getMessage())); + } + + @ExceptionHandler(OrderException.class) + public ResponseEntity> OrderException(OrderException e) { + log.error(e.getMessage(), e); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(BasicResponse.fail(e.getMessage())); + } + + @ExceptionHandler(AuthenticationCredentialsNotFoundException.class) public ResponseEntity> authenticationCredentialsNotFoundException( AuthenticationCredentialsNotFoundException e) { @@ -88,7 +103,7 @@ public ResponseEntity> authenticationCredentialsNotFoundEx @ExceptionHandler(AddRestaurantException.class) public ResponseEntity> addRestaurantException( AddRestaurantException e) { - log.error(e.getMessage(), e); + log.error(e.getMessage(), e.getCause()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(BasicResponse.fail(e.getMessage())); } } diff --git a/src/main/java/com/flab/doorrush/global/exception/KakaoApiResponseException.java b/src/main/java/com/flab/doorrush/global/exception/KakaoApiResponseException.java new file mode 100644 index 0000000..bce4041 --- /dev/null +++ b/src/main/java/com/flab/doorrush/global/exception/KakaoApiResponseException.java @@ -0,0 +1,8 @@ +package com.flab.doorrush.global.exception; + +public class KakaoApiResponseException extends RuntimeException { + + public KakaoApiResponseException(String message) { + super(message); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 35445d4..bcdd8ce 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,4 +16,7 @@ spring: # mybatis mybatis: - mapper-locations: classpath:mybatis/mapper/*Mapper.xml \ No newline at end of file + mapper-locations: classpath:mybatis/mapper/*Mapper.xml + +api: + authorization: ${AUTHORIZATION} \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index aea3474..8be25a2 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -63,4 +63,4 @@ VALUES ('1', '1', '짜장면', 7500), ('8', '3', '에비동', 9500), ('9', '3', '규동', 9000), ('10', '3', '믹스나베', 11500), - ('11', '3', '김치나베', 11000); + ('11', '3', '김치나베', 11000); \ No newline at end of file diff --git a/src/main/resources/mybatis/mapper/OrderMapper.xml b/src/main/resources/mybatis/mapper/OrderMapper.xml new file mode 100644 index 0000000..b48ddab --- /dev/null +++ b/src/main/resources/mybatis/mapper/OrderMapper.xml @@ -0,0 +1,58 @@ + + + + + + + + INSERT INTO `ORDER` (`USER_SEQ`, `ADDRESS`, `RESTAURANT_SEQ`, `RESTAURANT_NAME`, `ORDER_STATUS`, + `AMOUNT`, `ORDER_TIME`) + VALUES (#{userSeq}, + #{address}, + #{restaurantSeq}, + #{restaurantName}, + #{orderStatus}, + #{amount}, + NOW()) + + + + INSERT INTO `ORDER_MENU` (`MENU_SEQ`, `ORDER_SEQ`, `COUNT`) + VALUES + + (#{menu.menuSeq},#{menu.orderSeq},#{menu.count}) + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mybatis/mapper/RestaurantMapper.xml b/src/main/resources/mybatis/mapper/RestaurantMapper.xml index 4b8b2bf..5b09fc8 100644 --- a/src/main/resources/mybatis/mapper/RestaurantMapper.xml +++ b/src/main/resources/mybatis/mapper/RestaurantMapper.xml @@ -1,4 +1,5 @@ + @@ -16,7 +17,7 @@ ADDRESS_SEQ) VALUES ( #{ownerSeq} , #{category} - , #{openYN} + , #{openYn} , #{restaurantName} , #{introduction} , #{minimumOrderAmount} @@ -45,10 +46,7 @@ FROM RESTAURANT WHERE OWNER_SEQ = #{ownerSeq} AND CATEGORY = #{category} - AND OPEN_YN = #{openYN} AND RESTAURANT_NAME = #{restaurantName} - AND INTRODUCTION = #{introduction} - AND MINIMUM_ORDER_AMOUNT = #{minimumOrderAmount} AND ADDRESS_SEQ = #{addressSeq}; \ No newline at end of file diff --git a/src/main/resources/mybatis/mapper/UserAddressMapper.xml b/src/main/resources/mybatis/mapper/UserAddressMapper.xml index 771aab0..abf3231 100644 --- a/src/main/resources/mybatis/mapper/UserAddressMapper.xml +++ b/src/main/resources/mybatis/mapper/UserAddressMapper.xml @@ -66,6 +66,17 @@ WHERE ADDRESS_SEQ = #{addressSeq}) + +