Skip to content

Commit 6f9119f

Browse files
authored
Merge pull request #94 from Kimjipang/main
[volume-3] 도메인 모델링 및 구현
2 parents b075f0d + 7dc1b19 commit 6f9119f

46 files changed

Lines changed: 1177 additions & 21 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: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.loopers.application.like;
2+
3+
import com.loopers.domain.like.Like;
4+
import com.loopers.domain.like.LikeRepository;
5+
import com.loopers.domain.product.ProductRepository;
6+
import com.loopers.domain.user.UserRepository;
7+
import com.loopers.interfaces.api.like.LikeV1Dto;
8+
import com.loopers.support.error.CoreException;
9+
import com.loopers.support.error.ErrorType;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.stereotype.Component;
12+
import org.springframework.transaction.annotation.Transactional;
13+
14+
@Component
15+
@RequiredArgsConstructor
16+
public class LikeFacade {
17+
private final LikeRepository likeRepository;
18+
private final UserRepository userRepository;
19+
private final ProductRepository productRepository;
20+
21+
@Transactional
22+
public LikeInfo doLike(LikeV1Dto.LikeRequest request) {
23+
/*
24+
- [ ] 사용자 검증
25+
- [ ] 상품 검증
26+
- [ ] 좋아요 등록 (멱등)
27+
*/
28+
Long userId = request.userId();
29+
Long productId = request.productId();
30+
31+
userRepository.findById(userId).orElseThrow(
32+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하지 않는 유저입니다.")
33+
);
34+
35+
productRepository.findById(productId).orElseThrow(
36+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하지 않는 상품입니다.")
37+
);
38+
39+
return likeRepository.findByUserIdAndProductId(userId, productId)
40+
.map(LikeInfo::from)
41+
.orElseGet(() -> {
42+
Like newLike = request.toEntity();
43+
likeRepository.save(newLike);
44+
45+
return LikeInfo.from(newLike);
46+
});
47+
}
48+
49+
@Transactional
50+
public void doUnlike(Long userId, Long productId) {
51+
/*
52+
- [ ] 사용자 검증
53+
- [ ] 상품 검증
54+
- [ ] 좋아요 취소 (멱등)
55+
*/
56+
57+
userRepository.findById(userId).orElseThrow(
58+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하지 않는 유저입니다.")
59+
);
60+
61+
productRepository.findById(productId).orElseThrow(
62+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하지 않는 상품입니다.")
63+
);
64+
65+
likeRepository.findByUserIdAndProductId(userId, productId)
66+
.ifPresent(likeRepository::delete);
67+
68+
}
69+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.loopers.application.like;
2+
3+
import com.loopers.domain.like.Like;
4+
5+
public record LikeInfo(Long id, Long userId, Long productId) {
6+
public static LikeInfo from(Like like) {
7+
return new LikeInfo(
8+
like.getId(),
9+
like.getUserId(),
10+
like.getProductId()
11+
);
12+
}
13+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.application.orderitem.OrderItemInfo;
4+
import com.loopers.domain.order.Order;
5+
import com.loopers.domain.order.OrderRepository;
6+
import com.loopers.domain.orderitem.OrderItem;
7+
import com.loopers.domain.orderitem.OrderItemRepository;
8+
import com.loopers.domain.product.Product;
9+
import com.loopers.domain.product.ProductRepository;
10+
import com.loopers.domain.user.UserRepository;
11+
import com.loopers.interfaces.api.order.OrderV1Dto;
12+
import com.loopers.support.error.CoreException;
13+
import com.loopers.support.error.ErrorType;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.stereotype.Component;
16+
import org.springframework.transaction.annotation.Transactional;
17+
18+
import java.math.BigDecimal;
19+
import java.util.List;
20+
21+
@Component
22+
@RequiredArgsConstructor
23+
public class OrderFacade {
24+
private final OrderRepository orderRepository;
25+
private final OrderItemRepository orderItemRepository;
26+
private final UserRepository userRepository;
27+
private final ProductRepository productRepository;
28+
29+
@Transactional
30+
public OrderResultInfo createOrder(OrderV1Dto.OrderRequest request) {
31+
/*
32+
- [ ] 사용자 존재 여부 확인
33+
- [ ] OrderRequest 내에서 OrderItemRequest 목록을 순회하며 상품 존재 여부 확인
34+
- [ ] OrderItem 목록 생성
35+
*/
36+
37+
Long userId = request.userId();
38+
39+
userRepository.findById(userId).orElseThrow(
40+
() -> new CoreException(ErrorType.BAD_REQUEST, "존재하는 유저가 아닙니다.")
41+
);
42+
43+
List<OrderV1Dto.OrderItemRequest> orderItemRequests = request.orderItems();
44+
45+
List<OrderItem> orderItems = orderItemRequests.stream()
46+
.map(item -> {
47+
Long productId = item.productId();
48+
Product product = productRepository.findById(productId).orElseThrow(
49+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하는 상품이 아닙니다.")
50+
);
51+
52+
OrderItem orderItem = item.toEntity(
53+
null,
54+
product.getPrice().multiply(BigDecimal.valueOf(item.quantity()))
55+
);
56+
return orderItem;
57+
58+
})
59+
.toList();
60+
61+
BigDecimal totalPrice = orderItems.stream()
62+
.map(OrderItem::getOrderPrice)
63+
.reduce(BigDecimal.ZERO, BigDecimal::add);
64+
65+
Order order = request.toEntity(totalPrice);
66+
67+
Order saved = orderRepository.save(order);
68+
69+
orderItems.forEach(item -> item.assignOrderId(saved.getId()));
70+
orderItemRepository.saveAll(orderItems);
71+
72+
orderItems.forEach(item -> {
73+
Long productId = item.getProductId();
74+
75+
Product product = productRepository.findById(productId).orElseThrow(
76+
() -> new CoreException(ErrorType.NOT_FOUND, "존재하는 상품이 아닙니다.")
77+
);
78+
79+
product.decreaseStock(item.getQuantity());
80+
81+
});
82+
83+
List<OrderItemInfo> orderItemInfos = orderItems.stream()
84+
.map(orderItem -> OrderItemInfo.from(orderItem, orderItem.getOrderPrice()))
85+
.toList();
86+
87+
return new OrderResultInfo(OrderInfo.from(saved), orderItemInfos);
88+
}
89+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.domain.order.Order;
4+
import com.loopers.domain.order.OrderStatus;
5+
6+
import java.math.BigDecimal;
7+
import java.time.ZonedDateTime;
8+
9+
public record OrderInfo(
10+
Long id,
11+
Long userId,
12+
OrderStatus orderStatus,
13+
BigDecimal totalPrice,
14+
ZonedDateTime createdAt
15+
) {
16+
public static OrderInfo from(Order order) {
17+
return new OrderInfo(
18+
order.getId(),
19+
order.getUserId(),
20+
order.getOrderStatus(),
21+
order.getTotalPrice(),
22+
order.getCreatedAt()
23+
);
24+
}
25+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.loopers.application.order;
2+
3+
import com.loopers.application.orderitem.OrderItemInfo;
4+
5+
import java.util.List;
6+
7+
public record OrderResultInfo(
8+
OrderInfo orderInfo,
9+
List<OrderItemInfo> orderItemInfos
10+
) {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.loopers.application.orderitem;
2+
3+
import com.loopers.domain.orderitem.OrderItem;
4+
5+
import java.math.BigDecimal;
6+
7+
public record OrderItemInfo(Long id, Long orderId, Long productId, int quantity, BigDecimal orderPrice) {
8+
public static OrderItemInfo from(OrderItem orderItem, BigDecimal productPrice) {
9+
return new OrderItemInfo(
10+
orderItem.getId(),
11+
orderItem.getOrderId(),
12+
orderItem.getProductId(),
13+
orderItem.getQuantity(),
14+
productPrice.multiply(BigDecimal.valueOf(orderItem.getQuantity()))
15+
);
16+
}
17+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.loopers.application.product;
2+
3+
import com.loopers.domain.like.LikeRepository;
4+
import com.loopers.domain.product.Product;
5+
import com.loopers.domain.product.ProductRepository;
6+
import com.loopers.interfaces.api.product.ProductV1Dto;
7+
import com.loopers.support.error.CoreException;
8+
import com.loopers.support.error.ErrorType;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.stereotype.Component;
11+
import org.springframework.transaction.annotation.Transactional;
12+
13+
import java.util.List;
14+
15+
@Component
16+
@RequiredArgsConstructor
17+
public class ProductFacade {
18+
private final ProductRepository productRepository;
19+
private final LikeRepository likeRepository;
20+
21+
22+
@Transactional
23+
public ProductInfo registerProduct(ProductV1Dto.ProductRequest request) {
24+
Product product = request.toEntity();
25+
productRepository.save(product);
26+
27+
return ProductInfo.from(product, 0);
28+
}
29+
30+
@Transactional(readOnly = true)
31+
public List<ProductInfo> findAllProducts() {
32+
List<Product> products = productRepository.findAll();
33+
34+
return products.stream()
35+
.map(product -> {
36+
int likeCount = likeRepository.countByProductId(product.getId());
37+
return ProductInfo.from(product, likeCount);
38+
})
39+
.toList();
40+
}
41+
42+
@Transactional(readOnly = true)
43+
public ProductInfo findProductById(Long id) {
44+
Product product = productRepository.findById(id).orElseThrow(
45+
() -> new CoreException(ErrorType.NOT_FOUND, "찾고자 하는 상품이 존재하지 않습니다.")
46+
);
47+
48+
int likeCount = likeRepository.countByProductId(id);
49+
50+
return ProductInfo.from(product, likeCount);
51+
}
52+
53+
@Transactional(readOnly = true)
54+
public List<ProductInfo> searchProductsByCondition(ProductV1Dto.SearchProductRequest request) {
55+
request.sortCondition().conditionValidate();
56+
57+
List<Product> products = productRepository.searchProductsByCondition(request);
58+
59+
return products.stream()
60+
.map(product -> {
61+
int likeCount = likeRepository.countByProductId(product.getId());
62+
return ProductInfo.from(product, likeCount);
63+
})
64+
.toList();
65+
}
66+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.loopers.application.product;
2+
3+
import com.loopers.domain.product.Product;
4+
5+
import java.math.BigDecimal;
6+
7+
public record ProductInfo(Long id, Long brandId, String name, BigDecimal price, int stock, int likeCount) {
8+
public static ProductInfo from(Product product, int likeCount) {
9+
return new ProductInfo(
10+
product.getId(),
11+
product.getBrandId(),
12+
product.getName(),
13+
product.getPrice(),
14+
product.getStock(),
15+
likeCount
16+
);
17+
}
18+
}

apps/commerce-api/src/main/java/com/loopers/application/user/UserFacade.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ public UserInfo signUp(UserV1Dto.SignUpRequest request) {
2828
}
2929

3030
@Transactional(readOnly = true)
31-
public UserInfo getUserByLoginId(String loginId) {
31+
public UserInfo findUserByLoginId(String loginId) {
3232
boolean isExist = userService.existsByLoginId(loginId);
3333

3434
if (!isExist) {
3535
throw new CoreException(ErrorType.NOT_FOUND, "해당 유저ID의 사용자가 존재하지 않습니다.");
3636
}
3737

38-
UserEntity userEntity = userService.getUserByLoginId(loginId);
38+
UserEntity userEntity = userService.findUserByLoginId(loginId);
3939
return UserInfo.from(userEntity);
4040
}
4141
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.loopers.domain.brand;
2+
3+
import com.loopers.domain.BaseEntity;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.Table;
7+
import lombok.AccessLevel;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
@Getter
12+
@Entity
13+
@Table(name = "brand")
14+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
15+
public class Brand extends BaseEntity {
16+
@Column(name = "name", nullable = false)
17+
private String name;
18+
19+
@Column(name = "description", nullable = false)
20+
private String description;
21+
}

0 commit comments

Comments
 (0)