Skip to content

Commit c5aad6e

Browse files
authored
Merge pull request #141 from sylee6529/round5
Round5
2 parents 384face + 0877830 commit c5aad6e

File tree

69 files changed

+2832
-209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2832
-209
lines changed

apps/commerce-api/src/main/java/com/loopers/CommerceApiApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import org.springframework.boot.SpringApplication;
55
import org.springframework.boot.autoconfigure.SpringBootApplication;
66
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
7+
import org.springframework.scheduling.annotation.EnableScheduling;
78
import java.util.TimeZone;
89

10+
@EnableScheduling
911
@ConfigurationPropertiesScan
1012
@SpringBootApplication
1113
public class CommerceApiApplication {

apps/commerce-api/src/main/java/com/loopers/application/like/LikeFacade.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ public class LikeFacade {
1212

1313
private final LikeService likeService;
1414

15-
public void likeProduct(String memberId, Long productId) {
15+
public void likeProduct(Long memberId, Long productId) {
1616
likeService.like(memberId, productId);
1717
}
1818

19-
public void unlikeProduct(String memberId, Long productId) {
19+
public void unlikeProduct(Long memberId, Long productId) {
2020
likeService.unlike(memberId, productId);
2121
}
2222
}

apps/commerce-api/src/main/java/com/loopers/application/members/MemberFacade.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class MemberFacade {
1919
@Transactional
2020
public MemberInfo registerMember(String memberId, String email, String password, String birthDate, Gender gender) {
2121
Member member = memberService.registerMember(memberId, email, password, birthDate, gender);
22-
pointService.initializeMemberPoints(memberId);
22+
pointService.initializeMemberPoints(member.getId()); // Member PK 사용
2323
return MemberInfo.from(member);
2424
}
2525

apps/commerce-api/src/main/java/com/loopers/application/order/OrderCommand.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99
@Builder
1010
public class OrderCommand {
1111

12-
private final String memberId;
12+
private final Long memberId;
1313
private final List<OrderLineCommand> orderLines;
1414
private final Long memberCouponId;
1515

16-
public static OrderCommand of(String memberId, List<OrderLineCommand> orderLines) {
16+
public static OrderCommand of(Long memberId, List<OrderLineCommand> orderLines) {
1717
return OrderCommand.builder()
1818
.memberId(memberId)
1919
.orderLines(orderLines)
2020
.memberCouponId(null)
2121
.build();
2222
}
2323

24-
public static OrderCommand of(String memberId, List<OrderLineCommand> orderLines, Long memberCouponId) {
24+
public static OrderCommand of(Long memberId, List<OrderLineCommand> orderLines, Long memberCouponId) {
2525
return OrderCommand.builder()
2626
.memberId(memberId)
2727
.orderLines(orderLines)

apps/commerce-api/src/main/java/com/loopers/application/order/OrderInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
public class OrderInfo {
1515

1616
private final Long id;
17-
private final String memberId;
17+
private final Long memberId;
1818
private final Money totalPrice;
1919
private final List<OrderItemInfo> items;
2020
private final ZonedDateTime orderedAt;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.loopers.application.product;
2+
3+
import lombok.Getter;
4+
5+
import java.util.List;
6+
7+
@Getter
8+
public class CursorPageInfo<T> {
9+
10+
private final List<T> content;
11+
private final String nextCursor;
12+
private final boolean hasNext;
13+
14+
private CursorPageInfo(List<T> content, String nextCursor, boolean hasNext) {
15+
this.content = content;
16+
this.nextCursor = nextCursor;
17+
this.hasNext = hasNext;
18+
}
19+
20+
public static <T> CursorPageInfo<T> of(List<T> content, String nextCursor, boolean hasNext) {
21+
return new CursorPageInfo<>(content, nextCursor, hasNext);
22+
}
23+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.loopers.application.product;
2+
3+
import com.loopers.domain.product.enums.ProductSortCondition;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
7+
@Getter
8+
@Builder
9+
public class ProductCursorSearchCommand {
10+
11+
private final Long brandId;
12+
private final String keyword;
13+
private final ProductSortCondition sort;
14+
private final String cursor;
15+
private final int size;
16+
private final Long memberIdOrNull;
17+
18+
public static ProductCursorSearchCommand of(Long brandId, String keyword, ProductSortCondition sort, String cursor, int size, Long memberIdOrNull) {
19+
return ProductCursorSearchCommand.builder()
20+
.brandId(brandId)
21+
.keyword(keyword)
22+
.sort(sort)
23+
.cursor(cursor)
24+
.size(size)
25+
.memberIdOrNull(memberIdOrNull)
26+
.build();
27+
}
28+
}
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package com.loopers.application.product;
22

33
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5+
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
46
import com.loopers.domain.common.vo.Money;
57
import com.loopers.domain.product.vo.Stock;
68
import lombok.Builder;
79

810
@Builder
11+
@JsonDeserialize(builder = ProductDetailInfo.ProductDetailInfoBuilder.class)
912
public class ProductDetailInfo {
10-
13+
1114
private final Long id;
1215
private final String name;
1316
private final String description;
@@ -17,7 +20,7 @@ public class ProductDetailInfo {
1720
private final Stock stock;
1821
private final int likeCount;
1922
private final boolean isLikedByMember;
20-
23+
2124
public Long getId() { return id; }
2225
public String getName() { return name; }
2326
public String getDescription() { return description; }
@@ -26,7 +29,11 @@ public class ProductDetailInfo {
2629
public Money getPrice() { return price; }
2730
public Stock getStock() { return stock; }
2831
public int getLikeCount() { return likeCount; }
29-
32+
3033
@JsonProperty("likedByMember")
3134
public boolean isLikedByMember() { return isLikedByMember; }
35+
36+
@JsonPOJOBuilder(withPrefix = "")
37+
public static class ProductDetailInfoBuilder {
38+
}
3239
}
Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,129 @@
11
package com.loopers.application.product;
22

3+
import com.loopers.domain.like.service.LikeReadService;
34
import com.loopers.domain.product.service.ProductReadService;
45
import com.loopers.domain.product.command.ProductSearchFilter;
6+
import com.loopers.domain.product.enums.ProductSortCondition;
7+
import com.loopers.infrastructure.cache.ProductDetailCache;
8+
import com.loopers.infrastructure.cache.ProductListCache;
59
import lombok.RequiredArgsConstructor;
610
import org.springframework.data.domain.Page;
711
import org.springframework.data.domain.PageRequest;
812
import org.springframework.data.domain.Pageable;
913
import org.springframework.stereotype.Component;
1014
import org.springframework.transaction.annotation.Transactional;
1115

16+
import java.util.List;
17+
1218
@RequiredArgsConstructor
1319
@Component
1420
@Transactional
1521
public class ProductFacade {
1622

1723
private final ProductReadService productReadService;
24+
private final LikeReadService likeReadService;
25+
private final ProductDetailCache productDetailCache;
26+
private final ProductListCache productListCache;
1827

1928
@Transactional(readOnly = true)
2029
public Page<ProductSummaryInfo> getProducts(ProductSearchCommand command) {
30+
// Cache only for LIKES_DESC sort
31+
if (command.getSort() == ProductSortCondition.LIKES_DESC) {
32+
return productListCache.get(
33+
command.getBrandId(),
34+
command.getSort(),
35+
command.getPage(),
36+
command.getSize()
37+
).orElseGet(() -> {
38+
Page<ProductSummaryInfo> result = fetchProducts(command);
39+
productListCache.set(
40+
command.getBrandId(),
41+
command.getSort(),
42+
command.getPage(),
43+
command.getSize(),
44+
result
45+
);
46+
return result;
47+
});
48+
}
49+
50+
return fetchProducts(command);
51+
}
52+
53+
private Page<ProductSummaryInfo> fetchProducts(ProductSearchCommand command) {
2154
ProductSearchFilter filter = ProductSearchFilter.of(
55+
command.getBrandId(),
2256
command.getKeyword(),
2357
command.getSort()
2458
);
2559

2660
Pageable pageable = PageRequest.of(command.getPage(), command.getSize());
2761

2862
return productReadService.getProducts(
29-
filter,
30-
pageable,
63+
filter,
64+
pageable,
3165
command.getMemberIdOrNull()
3266
);
3367
}
3468

3569
@Transactional(readOnly = true)
36-
public ProductDetailInfo getProductDetail(Long productId, String memberIdOrNull) {
37-
return productReadService.getProductDetail(productId, memberIdOrNull);
70+
public CursorPageInfo<ProductSummaryInfo> getProductsByCursor(ProductCursorSearchCommand command) {
71+
ProductSearchFilter filter = ProductSearchFilter.of(
72+
command.getBrandId(),
73+
command.getKeyword(),
74+
command.getSort()
75+
);
76+
77+
return productReadService.getProductsByCursor(
78+
filter,
79+
command.getCursor(),
80+
command.getSize(),
81+
command.getMemberIdOrNull()
82+
);
83+
}
84+
85+
@Transactional(readOnly = true)
86+
public ProductDetailInfo getProductDetail(Long productId, Long memberIdOrNull) {
87+
// 1. 캐시에서 상품 정보 조회 (isLikedByMember=false인 상태)
88+
ProductDetailInfo cachedInfo = productDetailCache.get(productId)
89+
.orElseGet(() -> {
90+
// 캐시 miss: DB 조회 (isLikedByMember는 나중에 계산)
91+
ProductDetailInfo result = productReadService.getProductDetail(productId, null);
92+
productDetailCache.set(productId, result);
93+
return result;
94+
});
95+
96+
// 2. 로그인하지 않은 경우 바로 반환
97+
if (memberIdOrNull == null) {
98+
return cachedInfo; // isLikedByMember=false 그대로
99+
}
100+
101+
// 3. isLikedByMember만 동적 계산
102+
boolean isLiked = likeReadService.isLikedBy(memberIdOrNull, productId);
103+
104+
// 4. isLikedByMember 필드만 교체해서 반환
105+
return ProductDetailInfo.builder()
106+
.id(cachedInfo.getId())
107+
.name(cachedInfo.getName())
108+
.description(cachedInfo.getDescription())
109+
.brandName(cachedInfo.getBrandName())
110+
.brandDescription(cachedInfo.getBrandDescription())
111+
.price(cachedInfo.getPrice())
112+
.stock(cachedInfo.getStock())
113+
.likeCount(cachedInfo.getLikeCount())
114+
.isLikedByMember(isLiked) // ⭐ 동적 계산
115+
.build();
116+
}
117+
118+
@Transactional(readOnly = true)
119+
public List<ProductSummaryInfo> getPopularProducts(Long memberIdOrNull) {
120+
// 캐시 제거 - 순수 DB 조회
121+
return productReadService.getPopularProducts(memberIdOrNull);
122+
}
123+
124+
@Transactional(readOnly = true)
125+
public List<ProductSummaryInfo> getBrandPopularProducts(Long brandId, int limit, Long memberIdOrNull) {
126+
// 캐시 제거 - 순수 DB 조회
127+
return productReadService.getBrandPopularProducts(brandId, limit, memberIdOrNull);
38128
}
39129
}

apps/commerce-api/src/main/java/com/loopers/application/product/ProductSearchCommand.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@
77
@Getter
88
@Builder
99
public class ProductSearchCommand {
10-
10+
11+
private final Long brandId;
1112
private final String keyword;
1213
private final ProductSortCondition sort;
1314
private final int page;
1415
private final int size;
15-
private final String memberIdOrNull;
16-
17-
public static ProductSearchCommand of(String keyword, ProductSortCondition sort, int page, int size, String memberIdOrNull) {
16+
private final Long memberIdOrNull;
17+
18+
public static ProductSearchCommand of(Long brandId, String keyword, ProductSortCondition sort, int page, int size, Long memberIdOrNull) {
1819
return ProductSearchCommand.builder()
20+
.brandId(brandId)
1921
.keyword(keyword)
2022
.sort(sort)
2123
.page(page)

0 commit comments

Comments
 (0)