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

Week2 #4

Open
wants to merge 7 commits into
base: week1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
HELP.md
readme.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package com.ll.exam.final__2022_10_08;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@SpringBootApplication
@EnableScheduling
@EnableBatchProcessing
@EnableJpaAuditing
@EnableAsync
public class Final20221008Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.ll.exam.final__2022_10_08.app.base.initData;

import com.ll.exam.final__2022_10_08.app.cart.service.CartService;
import com.ll.exam.final__2022_10_08.app.member.entity.Member;
import com.ll.exam.final__2022_10_08.app.member.service.MemberService;
import com.ll.exam.final__2022_10_08.app.order.entity.Order;
import com.ll.exam.final__2022_10_08.app.order.repository.OrderRepository;
import com.ll.exam.final__2022_10_08.app.order.service.OrderService;
import com.ll.exam.final__2022_10_08.app.post.service.PostService;
import com.ll.exam.final__2022_10_08.app.product.entity.Product;
import com.ll.exam.final__2022_10_08.app.product.service.ProductService;
Expand All @@ -10,6 +14,10 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@Configuration
@Profile({"dev", "test"})
public class NotProdInitData {
Expand All @@ -19,7 +27,10 @@ public class NotProdInitData {
CommandLineRunner initData(
MemberService memberService,
PostService postService,
ProductService productService
ProductService productService,
CartService cartService,
OrderService orderService,
OrderRepository orderRepository
) {
return args -> {
if (initDataDone) {
Expand Down Expand Up @@ -69,6 +80,71 @@ CommandLineRunner initData(
Product product3 = productService.create(member1, "상품명3", 50_000, "REACT", "#IT #REACT");
Product product4 = productService.create(member2, "상품명4", 60_000, "HTML", "#IT #HTML");

memberService.addCash(member1, 10_000, "충전__무통장입금");
memberService.addCash(member1, 20_000, "충전__무통장입금");
memberService.addCash(member1, -5_000, "출금__일반");
memberService.addCash(member1, 1_000_000, "충전__무통장입금");

memberService.addCash(member2, 2_000_000, "충전__무통장입금");

class Helper {
public Order order(Member member, List<Product> products) {
for (int i = 0; i < products.size(); i++) {
Product product = products.get(i);

cartService.addItem(member, product);
}

return orderService.createFromCart(member);
}
}

Helper helper = new Helper();

Order order1 = helper.order(member1, Arrays.asList(
product1,
product2
)
);

int order1PayPrice = order1.calculatePayPrice();
orderService.payByRestCashOnly(order1);

// 강제로 order1의 결제날짜를 1시간 전으로 돌린다.
// 환불 테스트를 위해서
order1.setPayDate(LocalDateTime.now().minusHours(1));
orderRepository.save(order1);

Order order2 = helper.order(member2, Arrays.asList(
product3,
product4
)
);

orderService.payByRestCashOnly(order2);

Order order3 = helper.order(member2, Arrays.asList(
product1,
product2
)
);

cartService.addItem(member1, product3);
cartService.addItem(member1, product4);

Order order4 = helper.order(member2, Arrays.asList(
product3,
product4
)
);

orderService.payByRestCashOnly(order4);

Order order5 = helper.order(member2, Arrays.asList(
product3,
product4
)
);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.annotation.RequestScope;

import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -44,6 +45,10 @@ public Rq(HttpServletRequest req, HttpServletResponse resp) {
public String redirectToBackWithMsg(String msg) {
String url = req.getHeader("Referer");

if (StringUtils.hasText(url) == false) {
url = "/";
}

return redirectWithMsg(url, msg);
}

Expand Down Expand Up @@ -90,7 +95,7 @@ private static String msgWithTtl(String msg) {
return Ut.url.encode(msg) + ";ttl=" + new Date().getTime();
}

public String redirectWithErrorMsg(String url, RsData rsData) {
public static String redirectWithErrorMsg(String url, RsData rsData) {
url = Ut.url.modifyQueryParam(url, "errorMsg", msgWithTtl(rsData.getMsg()));

return "redirect:" + url;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.ll.exam.final__2022_10_08.app.cart.controller;

import com.ll.exam.final__2022_10_08.app.base.rq.Rq;
import com.ll.exam.final__2022_10_08.app.cart.entity.CartItem;
import com.ll.exam.final__2022_10_08.app.cart.service.CartService;
import com.ll.exam.final__2022_10_08.app.member.entity.Member;
import com.ll.exam.final__2022_10_08.app.product.entity.Product;
import com.ll.exam.final__2022_10_08.app.security.dto.MemberContext;
import com.ll.exam.final__2022_10_08.util.Ut;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Arrays;
import java.util.List;

@Controller
@RequiredArgsConstructor
@RequestMapping("/cart")
public class CartController {
private final CartService cartService;
private final Rq rq;

@GetMapping("/items")
@PreAuthorize("isAuthenticated()")
public String showItems(@AuthenticationPrincipal MemberContext memberContext, Model model) {
Member buyer = memberContext.getMember();

List<CartItem> items = cartService.getItemsByBuyer(buyer);

model.addAttribute("items", items);

return "cart/items";
}

@PostMapping("/addItem/{productId}")
@PreAuthorize("isAuthenticated()")
public String addItem(@PathVariable long productId) {
cartService.addItem(rq.getMember(), new Product((productId)));

return rq.redirectToBackWithMsg("장바구니에 추가되었습니다.");
}

@PostMapping("/removeItem/{productId}")
@PreAuthorize("isAuthenticated()")
public String removeItem(@PathVariable long productId) {
cartService.removeItem(rq.getMember(), new Product((productId)));

return rq.redirectToBackWithMsg("장바구니에서 삭제되었습니다.");
}

@PostMapping("/removeItems")
@PreAuthorize("isAuthenticated()")
public String removeItems(String ids) {
Member buyer = rq.getMember();

String[] idsArr = ids.split(",");

Arrays.stream(idsArr)
.mapToLong(Long::parseLong)
.forEach(id -> {
CartItem cartItem = cartService.findItemById(id).orElse(null);

if (cartService.actorCanDelete(buyer, cartItem)) {
cartService.removeItem(cartItem);
}
});

return "redirect:/cart/items?msg=" + Ut.url.encode("%d건의 품목을 삭제하였습니다.".formatted(idsArr.length));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.ll.exam.final__2022_10_08.app.cart.entity;

import com.ll.exam.final__2022_10_08.app.base.entity.BaseEntity;
import com.ll.exam.final__2022_10_08.app.member.entity.Member;
import com.ll.exam.final__2022_10_08.app.product.entity.Product;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.SuperBuilder;

import javax.persistence.Entity;
import javax.persistence.ManyToOne;

import static javax.persistence.FetchType.LAZY;

@Entity
@Getter
@Setter
@NoArgsConstructor
@SuperBuilder
@ToString(callSuper = true)
public class CartItem extends BaseEntity {
@ManyToOne(fetch = LAZY)
private Member buyer;
@ManyToOne(fetch = LAZY)
private Product product;

public CartItem(long id) {
super(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ll.exam.final__2022_10_08.app.cart.repository;

import com.ll.exam.final__2022_10_08.app.cart.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface CartItemRepository extends JpaRepository<CartItem, Long> {
Optional<CartItem> findByBuyerIdAndProductId(long buyerId, long productId);

boolean existsByBuyerIdAndProductId(long buyerId, long productId);

List<CartItem> findAllByBuyerId(long buyerId);

List<CartItem> findAllByBuyerIdAndProductIdIn(long buyerId, long[] productIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.ll.exam.final__2022_10_08.app.cart.service;

import com.ll.exam.final__2022_10_08.app.cart.entity.CartItem;
import com.ll.exam.final__2022_10_08.app.cart.repository.CartItemRepository;
import com.ll.exam.final__2022_10_08.app.member.entity.Member;
import com.ll.exam.final__2022_10_08.app.product.entity.Product;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CartService {
private final CartItemRepository cartItemRepository;

@Transactional
public CartItem addItem(Member buyer, Product product) {
CartItem oldCartItem = cartItemRepository.findByBuyerIdAndProductId(buyer.getId(), product.getId()).orElse(null);

if (oldCartItem != null) {
return oldCartItem;
}

CartItem cartItem = CartItem.builder()
.buyer(buyer)
.product(product)
.build();

cartItemRepository.save(cartItem);

return cartItem;
}

@Transactional
public boolean removeItem(Member buyer, Product product) {
CartItem oldCartItem = cartItemRepository.findByBuyerIdAndProductId(buyer.getId(), product.getId()).orElse(null);

if (oldCartItem != null) {
cartItemRepository.delete(oldCartItem);
return true;
}

return false;
}

public boolean hasItem(Member buyer, Product product) {
return cartItemRepository.existsByBuyerIdAndProductId(buyer.getId(), product.getId());
}

public List<CartItem> getItemsByBuyer(Member buyer) {
return cartItemRepository.findAllByBuyerId(buyer.getId());
}

@Transactional
public void removeItem(CartItem cartItem) {
cartItemRepository.delete(cartItem);
}

@Transactional
public void removeItem(
Member buyer,
Long productId
) {
Product product = new Product(productId);
removeItem(buyer, product);
}

public Optional<CartItem> findItemById(long id) {
return cartItemRepository.findById(id);
}

public boolean actorCanDelete(Member buyer, CartItem cartItem) {
return buyer.getId().equals(cartItem.getBuyer().getId());
}

public List<CartItem> getCartItemsByBuyerIdProductIdIn(long buyerId, long[] productIds) {
return cartItemRepository.findAllByBuyerIdAndProductIdIn(buyerId, productIds);
}
}
Loading