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

Feature/#173 letters keyword frequent #174

Merged
merged 3 commits into from
Dec 8, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
import org.springframework.web.bind.annotation.RestController;
import postman.bottler.global.response.ApiResponse;
import postman.bottler.keyword.dto.request.UserKeywordRequestDTO;
import postman.bottler.keyword.dto.response.FrequentKeywordsDTO;
import postman.bottler.keyword.dto.response.KeywordResponseDTO;
import postman.bottler.keyword.dto.response.UserKeywordResponseDTO;
import postman.bottler.keyword.service.KeywordService;
import postman.bottler.keyword.service.LetterKeywordService;
import postman.bottler.keyword.service.UserKeywordService;
import postman.bottler.user.auth.CustomUserDetails;

Expand All @@ -23,6 +25,7 @@ public class KeywordController {

private final UserKeywordService userKeywordService;
private final KeywordService keywordService;
private final LetterKeywordService letterKeywordService;

@Operation(
summary = "์œ ์ € ํ‚ค์›Œ๋“œ ๋ชฉ๋ก ์กฐํšŒ",
Expand Down Expand Up @@ -58,4 +61,16 @@ public ApiResponse<KeywordResponseDTO> getKeywordList() {
KeywordResponseDTO result = keywordService.getKeywords();
return ApiResponse.onSuccess(result);
}

@Operation(
summary = "์‚ฌ์šฉ์ž์˜ ์ž์ฃผ ์“ฐ๋Š” ํ‚ค์›Œ๋“œ ์กฐํšŒ",
description = "ํ˜„์žฌ ์‚ฌ์šฉ์ž์˜ ์ž์ฃผ ์“ฐ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค"
)
@GetMapping("/frequent")
public ApiResponse<FrequentKeywordsDTO> getTopFrequentKeywords(
@AuthenticationPrincipal CustomUserDetails userDetails
) {
FrequentKeywordsDTO result = letterKeywordService.getTopFrequentKeywords(userDetails.getUserId());
return ApiResponse.onSuccess(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package postman.bottler.keyword.dto.response;

import java.util.List;

public record FrequentKeywordsDTO(
List<String> keywords
) {
public static FrequentKeywordsDTO from(List<String> keywords) {
return new FrequentKeywordsDTO(
keywords
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package postman.bottler.keyword.infra;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
Expand Down Expand Up @@ -59,4 +60,22 @@ private List<Long> getRandomLetters(int limit, List<Long> excludedLetterIds) {
.limit(limit)
.fetch();
}

public List<LetterKeywordEntity> getFrequentKeywords(List<Long> letterIds) {
QLetterKeywordEntity letterKeyword = QLetterKeywordEntity.letterKeywordEntity;

return queryFactory
.select(Projections.constructor(LetterKeywordEntity.class,
letterKeyword.letterId.min(),
letterKeyword.keyword,
letterKeyword.isDeleted
))
.from(letterKeyword)
.where(letterKeyword.letterId.in(letterIds)
.and(letterKeyword.isDeleted.eq(false)))
.groupBy(letterKeyword.keyword)
.orderBy(letterKeyword.keyword.count().desc())
.limit(5)
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,11 @@ public List<Long> getMatchedLetters(List<String> userKeywords, List<Long> letter
public void markKeywordsAsDeleted(List<Long> letterIds) {
jdbcRepository.batchUpdateIsDeleted(letterIds);
}

@Override
public List<LetterKeyword> getFrequentKeywords(List<Long> letterIds) {
return queryDslRepository.getFrequentKeywords(letterIds).stream()
.map(LetterKeywordEntity::toDomain)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class LetterKeywordEntity {
public LetterKeywordEntity(Long letterId, String keyword, boolean isDeleted) {
this.letterId = letterId;
this.keyword = keyword;
this.isDeleted = false;
this.isDeleted = isDeleted;
}

public static LetterKeywordEntity from(LetterKeyword letterKeyword) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class AsyncRecommendationService {
private final RecommendService recommendService;
private final UserKeywordService userKeywordService;
private final LetterBoxService letterBoxService;
private static final int RECOMMENDATION_LIMIT = 3;
private static final int RECOMMENDATION_LIMIT = 10;
private final RedisLetterService redisLetterService;

@Async
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface LetterKeywordRepository {
List<Long> getMatchedLetters(List<String> userKeywords, List<Long> letterIds, int limit);

void markKeywordsAsDeleted(List<Long> letterIds);

List<LetterKeyword> getFrequentKeywords(List<Long> letterIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import postman.bottler.keyword.domain.LetterKeyword;
import postman.bottler.keyword.dto.response.FrequentKeywordsDTO;
import postman.bottler.letter.service.LetterService;

@Slf4j
@Service
@RequiredArgsConstructor
public class LetterKeywordService {

private final LetterKeywordRepository letterKeywordRepository;
private final LetterService letterService;

@Transactional
public void createLetterKeywords(Long letterId, List<String> keywords) {
Expand All @@ -33,4 +38,15 @@ public List<String> getKeywords(Long letterId) {
public void markKeywordsAsDeleted(List<Long> letterIds) {
letterKeywordRepository.markKeywordsAsDeleted(letterIds);
}


@Transactional(readOnly = true)
public FrequentKeywordsDTO getTopFrequentKeywords(Long userId) {
List<Long> letterIds = letterService.findAllByUserId(userId);
List<LetterKeyword> frequentKeywords = letterKeywordRepository.getFrequentKeywords(letterIds);
List<String> keywords = frequentKeywords.stream()
.map(LetterKeyword::getKeyword)
.toList();
return FrequentKeywordsDTO.from(keywords);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import postman.bottler.keyword.util.RedisLetterKeyUtil;
import postman.bottler.letter.domain.BoxType;
import postman.bottler.letter.domain.Letter;
import postman.bottler.letter.domain.LetterType;
import postman.bottler.letter.dto.LetterBoxDTO;
import postman.bottler.letter.exception.LetterNotFoundException;
Expand All @@ -26,11 +28,13 @@ public class RedisLetterService {
private static final int MAX_RECOMMENDATIONS = 3;
private final LetterService letterService;

@Transactional
public void saveRecommendationsTemp(Long userId, List<Long> recommendations) {
String key = RedisLetterKeyUtil.getTempRecommendationKey(userId);
redisTemplate.opsForValue().set(key, recommendations);
}

@Transactional
public RecommendNotificationRequestDTO updateRecommendationsFromTemp(Long userId) {
String tempKey = RedisLetterKeyUtil.getTempRecommendationKey(userId);
String activeKey = RedisLetterKeyUtil.getActiveRecommendationKey(userId);
Expand All @@ -41,10 +45,11 @@ public RecommendNotificationRequestDTO updateRecommendationsFromTemp(Long userId
Long recommendId = findFirstValidLetter(tempRecommendations);
updateActiveRecommendations(recommendId, activeRecommendations, activeKey);
saveLetterToBox(userId, recommendId);

Letter letter = letterService.findLetter(recommendId);

redisTemplate.delete(tempKey);

return RecommendNotificationRequestDTO.of(userId, recommendId);
return RecommendNotificationRequestDTO.of(userId, recommendId, letter.getLabel());
}

private void saveLetterToBox(Long userId, Long letterId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ public interface LetterJpaRepository extends JpaRepository<LetterEntity, Long> {

@Query("SELECT l FROM LetterEntity l WHERE l.id IN :letterIds AND l.isDeleted = false")
List<LetterEntity> findAllByIds(List<Long> letterIds);

@Query("SELECT l FROM LetterEntity l WHERE l.userId = :userId AND l.isDeleted = false")
List<LetterEntity> findAllByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,11 @@ public List<Letter> findAllByIds(List<Long> letterIds) {
public boolean checkLetterExists(Long letterId) {
return letterJpaRepository.existsById(letterId);
}

@Override
public List<Letter> findAllByUserId(Long userId) {
return letterJpaRepository.findAllByUserId(userId).stream()
.map(LetterEntity::toDomain)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public interface LetterRepository {
List<Letter> findAllByIds(List<Long> letterIds);

boolean checkLetterExists(Long letterId);

List<Letter> findAllByUserId(Long userId);
}
10 changes: 9 additions & 1 deletion src/main/java/postman/bottler/letter/service/LetterService.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ public ReceiverDTO getReceiverInfoById(Long letterId) {
return ReceiverDTO.from(letter);
}

private Letter findLetter(Long letterId) {
@Transactional(readOnly = true)
public Letter findLetter(Long letterId) {
return letterRepository.findById(letterId)
.orElseThrow(() -> new LetterNotFoundException("ํ‚ค์›Œ๋“œ ํŽธ์ง€๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."));
}
Expand All @@ -89,4 +90,11 @@ public List<NotificationLabelRequestDTO> getLabels(List<Long> ids) {
.map(find -> new NotificationLabelRequestDTO(find.getId(), find.getLabel()))
.toList();
}

@Transactional(readOnly = true)
public List<Long> findAllByUserId(Long userId) {
return letterRepository.findAllByUserId(userId).stream()
.map(Letter::getId)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

public record RecommendNotificationRequestDTO(
Long userId,
Long letterId
Long letterId,
String label
) {
public static RecommendNotificationRequestDTO of(Long userId, Long letterId) {
return new RecommendNotificationRequestDTO(userId, letterId);
public static RecommendNotificationRequestDTO of(Long userId, Long letterId, String label) {
return new RecommendNotificationRequestDTO(userId, letterId, label);
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ spring:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true
highlight_sql: true
show-sql: false
show-sql: true
data:
redis:
host: ${REDIS_HOST}
Expand Down
Loading