Skip to content

Commit 817384d

Browse files
Merge pull request #435 from Podo-Store/develop
[FIX] 조회수 정렬 버그 해결
2 parents faa5550 + 3c089b1 commit 817384d

File tree

4 files changed

+85
-75
lines changed

4 files changed

+85
-75
lines changed

gradlew

100644100755
File mode changed.

src/main/java/PodoeMarket/podoemarket/product/service/ProductService.java

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.io.OutputStream;
3838
import java.net.URL;
3939
import java.time.LocalDateTime;
40+
import java.util.Comparator;
4041
import java.util.List;
4142
import java.util.UUID;
4243
import java.util.zip.ZipEntry;
@@ -58,37 +59,37 @@ public class ProductService {
5859

5960
public List<ScriptListResponseDTO.ProductListDTO> getPlayList(int page, UserEntity userInfo, PlayType playType, int pageSize, ProductSortType sortType) {
6061
try {
61-
Sort sort = createProductSort(sortType);
62-
final Pageable pageable = PageRequest.of(page, pageSize, sort);
63-
final List<ProductEntity> plays = productRepo.findAllValidPlays(playType, ProductStatus.PASS, pageable);
64-
65-
return plays.stream()
66-
.map(play -> {
67-
final ScriptListResponseDTO.ProductListDTO productListDTO = new ScriptListResponseDTO.ProductListDTO();
68-
69-
final String scriptImage = generateScriptImgURL(play);
70-
71-
productListDTO.setId(play.getId());
72-
productListDTO.setTitle(play.getTitle());
73-
productListDTO.setWriter(play.getWriter());
74-
productListDTO.setImagePath(scriptImage);
75-
productListDTO.setScript(play.getScript());
76-
productListDTO.setScriptPrice(play.getScriptPrice());
77-
productListDTO.setPerformance(play.getPerformance());
78-
productListDTO.setPerformancePrice(play.getPerformancePrice());
79-
productListDTO.setDate(play.getCreatedAt());
80-
productListDTO.setChecked(play.getChecked());
81-
productListDTO.setLike(getProductLikeStatus(userInfo, play.getId()));
82-
productListDTO.setLikeCount(play.getLikeCount());
83-
productListDTO.setViewCount(viewCountService.getProductViewCount(play.getId()));
84-
85-
return productListDTO;
86-
}).toList();
62+
// POPULAR(조회수 기준 정렬)는 Java단에서 처리
63+
if (sortType == ProductSortType.POPULAR) {
64+
List<ProductEntity> plays = productRepo.findAllValidPlays(
65+
playType,
66+
ProductStatus.PASS,
67+
PageRequest.of(page, pageSize, Sort.unsorted()) // 정렬 직접 처리
68+
);
69+
70+
return plays.stream()
71+
.map(play -> getListDto(userInfo, play))
72+
.sorted(Comparator.comparingLong(ScriptListResponseDTO.ProductListDTO::getViewCount).reversed())
73+
.limit(pageSize)
74+
.toList();
75+
} else {
76+
Sort sort = createProductSort(sortType);
77+
List<ProductEntity> plays = productRepo.findAllValidPlays(
78+
playType,
79+
ProductStatus.PASS,
80+
PageRequest.of(page, pageSize, sort)
81+
);
82+
83+
return plays.stream()
84+
.map(play -> getListDto(userInfo, play))
85+
.toList();
86+
}
8787
} catch (Exception e) {
88-
throw e;
88+
throw new RuntimeException("작품 목록 조회 실패", e);
8989
}
9090
}
9191

92+
9293
public ProductEntity getProduct(UUID id) {
9394
try {
9495
return productRepo.findById(id);
@@ -612,4 +613,26 @@ protected void createReviewLike(final ReviewLikeEntity like, final UUID reviewId
612613
throw new RuntimeException("좋아요 생성 실패", e);
613614
}
614615
}
616+
617+
private ScriptListResponseDTO.ProductListDTO getListDto(final UserEntity userInfo, final ProductEntity play) {
618+
ScriptListResponseDTO.ProductListDTO productListDTO = new ScriptListResponseDTO.ProductListDTO();
619+
620+
final String scriptImage = generateScriptImgURL(play);
621+
622+
productListDTO.setId(play.getId());
623+
productListDTO.setTitle(play.getTitle());
624+
productListDTO.setWriter(play.getWriter());
625+
productListDTO.setImagePath(scriptImage);
626+
productListDTO.setScript(play.getScript());
627+
productListDTO.setScriptPrice(play.getScriptPrice());
628+
productListDTO.setPerformance(play.getPerformance());
629+
productListDTO.setPerformancePrice(play.getPerformancePrice());
630+
productListDTO.setDate(play.getCreatedAt());
631+
productListDTO.setChecked(play.getChecked());
632+
productListDTO.setLike(getProductLikeStatus(userInfo, play.getId()));
633+
productListDTO.setLikeCount(play.getLikeCount());
634+
productListDTO.setViewCount(viewCountService.getProductViewCount(play.getId()));
635+
636+
return productListDTO;
637+
}
615638
}

src/main/java/PodoeMarket/podoemarket/product/type/ProductSortType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
@Getter
88
@AllArgsConstructor
99
public enum ProductSortType {
10-
POPULAR("viewCount", Sort.Direction.DESC, true),
10+
POPULAR("viewCount", Sort.Direction.DESC, true), // JPA 정렬 무시
1111
LIKE_COUNT("likeCount", Sort.Direction.DESC, true),
1212
LATEST("createdAt", Sort.Direction.DESC, false);
1313

src/main/java/PodoeMarket/podoemarket/service/ViewCountService.java

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import org.springframework.stereotype.Service;
1414

1515
import java.nio.charset.StandardCharsets;
16-
import java.util.Set;
1716
import java.util.UUID;
1817

1918
@Slf4j
@@ -23,23 +22,25 @@ public class ViewCountService {
2322
private final StringRedisTemplate redisTemplate;
2423
private final ProductRepository productRepo;
2524

26-
// 비로그인 사용자의 조회 확인 (쿠키는 컨트롤러에서 처리)
25+
private static final String DELTA_PREFIX = "product:views:delta:";
26+
27+
// 조회수 증가 (Delta만)
2728
public void incrementViewForProduct(UUID productId) {
28-
final String deltaKey = "product:views:delta:" + productId.toString();
29+
final String deltaKey = DELTA_PREFIX + productId.toString();
2930
redisTemplate.opsForValue().increment(deltaKey);
3031
}
3132

32-
// 상품의 현재 조회수 조회(DB 기준선 + 델타)
33+
// 조회수 조회 (DB + Delta)
3334
public Long getProductViewCount(UUID productId) {
3435
long base = 0L;
3536
final ProductEntity product = productRepo.findById(productId);
3637

3738
if(product != null && product.getViewCount() != null)
3839
base = product.getViewCount();
3940

40-
final String deltaViewCountKey = "product:views:delta:" + productId.toString();
41-
final String deltaCount = redisTemplate.opsForValue().get(deltaViewCountKey);
42-
final long delta = (deltaCount != null) ? Long.parseLong(deltaCount) : 0L;
41+
final String deltaKey = DELTA_PREFIX + productId.toString();
42+
final String deltaStr = redisTemplate.opsForValue().get(deltaKey);
43+
final long delta = (deltaStr != null) ? Long.parseLong(deltaStr) : 0L;
4344

4445
return base + delta;
4546
}
@@ -49,50 +50,36 @@ public Long getProductViewCount(UUID productId) {
4950
@Transactional
5051
public void flushDeltaToDB() {
5152
log.info("작품 조회수 델타 동기화 시작");
52-
RedisConnection conn = null;
53-
try {
54-
conn = redisTemplate.getConnectionFactory().getConnection();
55-
56-
ScanOptions options = ScanOptions.scanOptions()
57-
.match("product:views:delta:*")
58-
.count(500)
59-
.build();
60-
61-
try(Cursor<byte[]> cursor = conn.scan(options)) {
62-
while(cursor.hasNext()) {
63-
String key = new String(cursor.next(), StandardCharsets.UTF_8);
64-
String idStr = key.substring("product:views:delta:".length());
65-
UUID productId = UUID.fromString(idStr);
66-
67-
// 원자적 GET+DEL (지원 시)
68-
String deltaStr = redisTemplate.opsForValue().getAndDelete(key);
69-
70-
if(deltaStr == null)
71-
continue;
72-
73-
long delta;
74-
try {
75-
delta = Long.parseLong(deltaStr);
76-
} catch (NumberFormatException e) {
77-
continue;
78-
}
79-
80-
if(delta <=0)
81-
continue;
82-
83-
// DB += delta
84-
productRepo.incrementViewCount(productId, delta);
53+
54+
RedisConnection conn = redisTemplate.getConnectionFactory().getConnection();
55+
try (Cursor<byte[]> cursor = conn.scan(
56+
ScanOptions.scanOptions().match(DELTA_PREFIX + "*").count(500).build())){
57+
58+
while(cursor.hasNext()) {
59+
String key = new String(cursor.next(), StandardCharsets.UTF_8);
60+
UUID productId = UUID.fromString(key.substring(DELTA_PREFIX.length()));
61+
String deltaStr = redisTemplate.opsForValue().getAndDelete(key);
62+
63+
if(deltaStr == null)
64+
continue;
65+
66+
long delta;
67+
try {
68+
delta = Long.parseLong(deltaStr);
69+
} catch (NumberFormatException e) {
70+
continue;
8571
}
72+
73+
if(delta <=0)
74+
continue;
75+
76+
// DB += delta
77+
productRepo.incrementViewCount(productId, delta);
8678
}
87-
log.info("작품 조회수 델타 동기화 완료");
8879
} catch (Exception e) {
89-
log.error("작품 조회수 동기화 중 오류 발생: ", e);
80+
log.error("조회수 동기화 중 오류 발생: ", e);
9081
} finally {
91-
if (conn != null) {
92-
try {
93-
conn.close();
94-
} catch (Exception ignore) {}
95-
}
82+
conn.close();
9683
}
9784
}
9885
}

0 commit comments

Comments
 (0)