Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] 장바구니 api 구현 #145

Merged
merged 8 commits into from
Dec 2, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package org.tattour.server.domain.cart.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
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;
import org.tattour.server.domain.cart.controller.dto.request.SaveCartReq;
import org.tattour.server.domain.cart.controller.dto.response.CartItemsRes;
import org.tattour.server.domain.cart.facade.CartFacade;
import org.tattour.server.global.config.annotations.UserId;
import org.tattour.server.global.dto.BaseResponse;
import org.tattour.server.global.dto.FailResponse;
import org.tattour.server.global.dto.SuccessResponse;
import org.tattour.server.global.dto.SuccessType;

@RestController
@RequestMapping("/api/v1/cart")
@SecurityRequirement(name = "JWT Auth")
@Tag(name = "Cart(장바구니)", description = "Cart API Document")
@RequiredArgsConstructor
@Validated
public class CartController {
private final CartFacade cartFacade;

@Operation(summary = "장바구니 아이템 추가")
@ApiResponses(value = {
@ApiResponse(
responseCode = "201",
description = "생성에 성공했습니다.",
content = @Content(schema = @Schema(implementation = SuccessResponse.class))),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 유저입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 스티커입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "500",
description = "알 수 없는 서버 에러가 발생했습니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class)))
})
@PostMapping("/items")
public ResponseEntity<?> saveCartItem(
@Parameter(hidden = true) @UserId Integer userId,
@RequestBody @Valid SaveCartReq saveCartReq
) {
cartFacade.saveCartItem(userId, saveCartReq);
return BaseResponse.success(SuccessType.CREATE_SUCCESS);
}

@Operation(summary = "유저 장바구니 목록 조회")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "조회에 성공했습니다.",
content = @Content(schema = @Schema(implementation = CartItemsRes.class))),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "500",
description = "알 수 없는 서버 에러가 발생했습니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class)))
})
@GetMapping("/items")
public ResponseEntity<?> getUserCartItem(
@Parameter(hidden = true) @UserId Integer userId
) {
return BaseResponse.success(SuccessType.GET_SUCCESS, cartFacade.getUserCartItems(userId));
}

@Operation(summary = "장바구니 수량 1개 더하기")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "갱신에 성공했습니다.",
content = @Content(schema = @Schema(implementation = SuccessResponse.class))),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 장바구니입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "500",
description = "알 수 없는 서버 에러가 발생했습니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class)))
})
@PatchMapping("/items/{cartId}")
public ResponseEntity<?> addCartCount(
@Parameter(hidden = true) @UserId Integer userId,
@PathVariable Integer cartId) {
cartFacade.increaseCartCount(userId, cartId);
return BaseResponse.success(SuccessType.UPDATE_SUCCESS);
}

@Operation(summary = "장바구니 삭제")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "삭제에 성공했습니다.",
content = @Content(schema = @Schema(implementation = SuccessResponse.class))),
@ApiResponse(
responseCode = "400",
description = "잘못된 요청입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 장바구니입니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class))),
@ApiResponse(
responseCode = "500",
description = "알 수 없는 서버 에러가 발생했습니다.",
content = @Content(schema = @Schema(implementation = FailResponse.class)))
})
@DeleteMapping("/items/{cartId}")
public ResponseEntity<?> deleteCartCount(
@Parameter(hidden = true) @UserId Integer userId,
@PathVariable Integer cartId) {
cartFacade.deleteCartItem(userId, cartId);
return BaseResponse.success(SuccessType.DELETE_SUCCESS);
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.tattour.server.domain.cart.controller.dto.request;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class SaveCartReq {
@NotNull
private int stickerId;

@NotNull
@Min(1)
private int count;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.tattour.server.domain.cart.controller.dto.response;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class CartItemRes {
private int stickerId;
private String mainImageUrl;
private String name;
private int price;
private int discountPrice;
private int count;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.tattour.server.domain.cart.controller.dto.response;

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.tattour.server.domain.order.provider.vo.OrderAmountDetailRes;

@Getter
@Setter
@AllArgsConstructor
public class CartItemsRes {
private List<CartItemRes> cartItemsRes;

private OrderAmountDetailRes orderAmountDetailRes;

public static CartItemsRes of(List<CartItemRes> cartItemsRes, OrderAmountDetailRes orderAmountDetailRes) {
return new CartItemsRes(cartItemsRes, orderAmountDetailRes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.tattour.server.domain.cart.domain;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.tattour.server.domain.sticker.domain.Sticker;
import org.tattour.server.domain.user.domain.User;

@Entity
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Cart {
private final static int COUNT_INCREMENT = 1;

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sticker_id")
private Sticker sticker;

public void addCount(int count) {
this.count += count;
}

public void increaseCount() {
this.count += COUNT_INCREMENT;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.tattour.server.domain.cart.facade;

import org.tattour.server.domain.cart.controller.dto.request.SaveCartReq;
import org.tattour.server.domain.cart.controller.dto.response.CartItemsRes;

public interface CartFacade {
void saveCartItem(int userId, SaveCartReq req);

CartItemsRes getUserCartItems(int userId);

void increaseCartCount(int userId, int cartId);

void deleteCartItem(int userId, int cartId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.tattour.server.domain.cart.facade.impl;

import static org.tattour.server.domain.order.facade.impl.OrderFacadeImpl.SHIPPING_FEE;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.tattour.server.domain.cart.controller.dto.request.SaveCartReq;
import org.tattour.server.domain.cart.controller.dto.response.CartItemsRes;
import org.tattour.server.domain.cart.domain.Cart;
import org.tattour.server.domain.cart.facade.CartFacade;
import org.tattour.server.domain.cart.service.CartService;
import org.tattour.server.domain.sticker.domain.Sticker;
import org.tattour.server.domain.sticker.provider.StickerProvider;
import org.tattour.server.domain.sticker.provider.vo.StickerOrderInfo;
import org.tattour.server.domain.user.domain.User;
import org.tattour.server.domain.user.service.UserService;
import org.tattour.server.global.util.EntityDtoMapper;

@Service
@RequiredArgsConstructor
public class CartFacadeImpl implements CartFacade {
private final CartService cartService;
private final UserService userService;
private final StickerProvider stickerProvider;

@Override
@Transactional
public void saveCartItem(int userId, SaveCartReq req) {
User user = userService.readUserById(userId);
Sticker sticker = stickerProvider.getById(req.getStickerId());

cartService.mergeOrAddToCart(user, sticker, req.getCount());
}

//todo : 배송 지역별 배송비 책정
@Override
public CartItemsRes getUserCartItems(int userId) {
List<Cart> carts = cartService.findByUserId(userId);
StickerOrderInfo stickerOrderInfo = stickerProvider.getStickerOrderInfoFromCart(carts);

return CartItemsRes.of(
EntityDtoMapper.INSTANCE.toCartItemsRes(carts),
EntityDtoMapper.INSTANCE.toOrderAmountRes(stickerOrderInfo, SHIPPING_FEE));
}

@Override
public void increaseCartCount(int userId, int cartId) {
Cart cart = cartService.findByIdAndUserId(userId, cartId);
cartService.increaseCartCount(cart);
}

@Override
public void deleteCartItem(int userId, int cartId) {
Cart cart = cartService.findByIdAndUserId(userId, cartId);
cartService.delete(cart);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.tattour.server.domain.cart.repository;

import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.tattour.server.domain.cart.domain.Cart;
import org.tattour.server.domain.sticker.domain.Sticker;
import org.tattour.server.domain.user.domain.User;

@Repository
public interface CartRepository extends JpaRepository<Cart, Integer> {
List<Cart> findByUser_Id(int userId);

Optional<Cart> findByIdAndUser_Id(int id, int userId);

Optional<Cart> findCartByUserAndSticker(User user, Sticker sticker);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.tattour.server.domain.cart.service;

import java.util.List;
import java.util.Optional;
import org.tattour.server.domain.cart.domain.Cart;
import org.tattour.server.domain.sticker.domain.Sticker;
import org.tattour.server.domain.user.domain.User;

public interface CartService {
List<Cart> findByUserId(int userId);

Optional<Cart> findByUserAndSticker(User user, Sticker sticker);

Cart findByIdAndUserId(int userId, int cartId);

void mergeOrAddToCart(User user, Sticker sticker, int count);

Cart createNewCart(User user, Sticker sticker, int count);

void increaseCartCount(Cart cart);

void delete(Cart cart);
}
Loading