Skip to content

Commit 3eb431e

Browse files
authored
Merge pull request #84 from yeonjiyeon/feature/week3
Round 3 - 이커머스 도메인 구현
2 parents 3f514fc + 9b22c3e commit 3eb431e

53 files changed

Lines changed: 2203 additions & 42 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.loopers.application.brand;
2+
3+
import com.loopers.domain.brand.Brand;
4+
import com.loopers.domain.brand.BrandService;
5+
import com.loopers.domain.product.Product;
6+
import com.loopers.domain.product.ProductService;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.data.domain.Page;
9+
import org.springframework.data.domain.Pageable;
10+
import org.springframework.stereotype.Component;
11+
12+
@RequiredArgsConstructor
13+
@Component
14+
public class BrandFacade {
15+
private final BrandService brandService;
16+
private final ProductService productService;
17+
18+
public BrandInfo getBrandInfo(long brandId, Pageable pageable) {
19+
Brand brand = brandService.getBrand(brandId);
20+
Page<Product> products = productService.getProductsByBrandId(brandId, pageable);
21+
return BrandInfo.from(brand, products);
22+
}
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.loopers.application.brand;
2+
3+
import com.loopers.domain.brand.Brand;
4+
import com.loopers.domain.product.Product;
5+
import com.loopers.application.product.ProductInfo;
6+
import org.springframework.data.domain.Page;
7+
8+
public record BrandInfo(
9+
Long brandId,
10+
String brandName,
11+
String brandDescription,
12+
13+
Page<ProductInfo> products
14+
) {
15+
16+
public static BrandInfo from(Brand brand, Page<Product> products) {
17+
18+
Page<ProductInfo> productInfos = products.map(ProductInfo::from);
19+
20+
return new BrandInfo(
21+
brand.getId(),
22+
brand.getName(),
23+
brand.getDescription(),
24+
productInfos
25+
);
26+
}
27+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.loopers.application.like;
2+
3+
import com.loopers.domain.like.Like;
4+
import com.loopers.domain.like.LikeService;
5+
import com.loopers.domain.product.ProductService;
6+
import com.loopers.domain.user.UserService;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.stereotype.Component;
9+
10+
@RequiredArgsConstructor
11+
@Component
12+
public class LikeFacade {
13+
14+
private final UserService userService;
15+
private final ProductService productService;
16+
private final LikeService likeService;
17+
18+
public LikeInfo like(long userId, long productId) {
19+
Like like = likeService.like(userId, productId);
20+
long totalLikes = likeService.countLikesByProductId(productId);
21+
return LikeInfo.from(like, totalLikes);
22+
}
23+
24+
public long unLike(long userId, long productId) {
25+
likeService.unLike(userId, productId);
26+
return likeService.countLikesByProductId(productId);
27+
}
28+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.loopers.application.like;
2+
3+
import com.loopers.domain.like.Like;
4+
5+
public record LikeInfo(
6+
Long id,
7+
Long userId,
8+
Long productId,
9+
long totalLikes
10+
) {
11+
12+
public static LikeInfo from(Like like, long totalLikes) {
13+
return new LikeInfo(
14+
like.getId(),
15+
like.getUserId(),
16+
like.getProductId(),
17+
totalLikes
18+
);
19+
}
20+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.domain.order.Order;
4+
import com.loopers.domain.order.OrderItem;
5+
import com.loopers.domain.order.OrderService;
6+
import com.loopers.domain.point.PointService;
7+
import com.loopers.domain.product.Product;
8+
import com.loopers.domain.product.ProductService;
9+
import com.loopers.domain.user.User;
10+
import com.loopers.domain.user.UserService;
11+
import com.loopers.interfaces.order.OrderV1Dto.OrderItemRequest;
12+
import com.loopers.interfaces.order.OrderV1Dto.OrderRequest;
13+
import com.loopers.support.error.CoreException;
14+
import com.loopers.support.error.ErrorType;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.stream.Collectors;
18+
import lombok.RequiredArgsConstructor;
19+
import org.springframework.stereotype.Component;
20+
import org.springframework.transaction.annotation.Transactional;
21+
22+
@RequiredArgsConstructor
23+
@Component
24+
public class OrderFacade {
25+
private final ProductService productService;
26+
private final UserService userService;
27+
private final OrderService orderService;
28+
private final PointService pointService;
29+
30+
@Transactional
31+
public OrderInfo placeOrder(String userId, OrderRequest request) {
32+
33+
if (userId == null) {
34+
throw new CoreException(ErrorType.BAD_REQUEST, "사용자 ID는 필수입니다.");
35+
}
36+
37+
User user = userService.getUser(userId);
38+
39+
List<Long> productIds = request.items().stream()
40+
.map(OrderItemRequest::productId)
41+
.toList();
42+
43+
List<Product> products = productService.getProducts(productIds);
44+
long totalAmount = orderService.calculateTotal(products, request.items());
45+
46+
47+
productService.deductStock(products, request.items());
48+
49+
pointService.deductPoint(user.getId(), totalAmount);
50+
51+
List<OrderItem> orderItems = buildOrderItems(products, request.items());
52+
Order order = orderService.createOrder(user.getId(), orderItems);
53+
54+
return OrderInfo.from(order);
55+
}
56+
57+
private List<OrderItem> buildOrderItems(List<Product> products, List<OrderItemRequest> items) {
58+
59+
Map<Long, Product> productMap = products.stream()
60+
.collect(Collectors.toMap(Product::getId, p -> p));
61+
62+
return items.stream()
63+
.map(i -> new OrderItem(productMap.get(i.productId()), i.quantity()))
64+
.toList();
65+
}
66+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.domain.money.Money;
4+
import com.loopers.domain.order.Order;
5+
import com.loopers.domain.order.OrderItem;
6+
import java.util.List;
7+
8+
public record OrderInfo(
9+
Long orderId,
10+
Long userId,
11+
List<OrderItemInfo> items,
12+
long totalAmount
13+
) {
14+
15+
public static OrderInfo from(Order order) {
16+
return new OrderInfo(
17+
order.getId(),
18+
order.getUserId(),
19+
order.getOrderItems().stream().map(OrderItemInfo::from).toList(),
20+
order.calculateTotalAmount()
21+
);
22+
}
23+
24+
public record OrderItemInfo(Long productId, int quantity, Money price) {
25+
26+
public static OrderItemInfo from(OrderItem item) {
27+
return new OrderItemInfo(
28+
item.getProductId(),
29+
item.getQuantity(),
30+
item.getPrice()
31+
);
32+
}
33+
}
34+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.loopers.application.product;
2+
3+
import com.loopers.domain.brand.BrandService;
4+
import com.loopers.domain.product.Product;
5+
import com.loopers.domain.product.ProductService;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.data.domain.Page;
8+
import org.springframework.data.domain.Pageable;
9+
import org.springframework.stereotype.Component;
10+
11+
@RequiredArgsConstructor
12+
@Component
13+
public class ProductFacade {
14+
private final ProductService productService;
15+
private final BrandService brandService;
16+
17+
public Page<ProductInfo> getProductInfo(Pageable pageable) {
18+
Page<Product> products = productService.getProducts(pageable);
19+
return products.map(product -> {
20+
String brandName = brandService.getBrand(product.getBrandId())
21+
.getName();
22+
return ProductInfo.from(product, brandName);
23+
});
24+
}
25+
26+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.loopers.application.product;
2+
3+
import com.loopers.domain.money.Money;
4+
import com.loopers.domain.product.Product;
5+
6+
public record ProductInfo(
7+
Long id,
8+
String name,
9+
Money price,
10+
String brandName
11+
) {
12+
public static ProductInfo from(Product product) {
13+
return new ProductInfo(
14+
product.getId(),
15+
product.getName(),
16+
product.getPrice(),
17+
null
18+
);
19+
}
20+
21+
public static ProductInfo from(Product product, String brandName) {
22+
return new ProductInfo(
23+
product.getId(),
24+
product.getName(),
25+
product.getPrice(),
26+
brandName
27+
);
28+
}
29+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.loopers.domain.brand;
2+
3+
import com.loopers.domain.BaseEntity;
4+
import com.loopers.support.error.CoreException;
5+
import com.loopers.support.error.ErrorType;
6+
import jakarta.persistence.Column;
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.Table;
9+
import lombok.AccessLevel;
10+
import lombok.NoArgsConstructor;
11+
12+
@Entity
13+
@Table(name = "brand")
14+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
15+
public class Brand extends BaseEntity {
16+
17+
@Column(nullable = false)
18+
private String name;
19+
20+
@Column(nullable = false)
21+
private String description;
22+
23+
public Brand(String name, String description) {
24+
if (name == null || name.isBlank()) {
25+
throw new CoreException(ErrorType.BAD_REQUEST, "이름은 필수입니다.");
26+
}
27+
28+
if (description == null || description.isBlank()) {
29+
throw new CoreException(ErrorType.BAD_REQUEST, "설명은 필수입니다.");
30+
}
31+
32+
this.name = name;
33+
this.description = description;
34+
}
35+
36+
public String getName() {
37+
return name;
38+
}
39+
40+
public String getDescription() {
41+
return description;
42+
}
43+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.loopers.domain.brand;
2+
3+
import java.util.Optional;
4+
5+
public interface BrandRepository {
6+
Brand save(Brand brand);
7+
8+
Optional<Brand> findById(Long id);
9+
}

0 commit comments

Comments
 (0)