Skip to content

Commit f67af1b

Browse files
committed
hotfix: 추천 생성 시 이전 추천 누적 및 MBTI 유형별 동일 추천 반환 문제 수정
1 parent 120c82e commit f67af1b

5 files changed

Lines changed: 91 additions & 6 deletions

File tree

src/main/java/org/umc/valuedi/domain/savings/repository/RecommendationRepository.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,12 @@ List<Recommendation> findAllByMemberIdAndMemberMbtiTestId(
4646
@Param("memberId") Long memberId,
4747
@Param("memberMbtiTestId") Long memberMbtiTestId
4848
);
49+
50+
@Query("""
51+
select r
52+
from Recommendation r
53+
left join fetch r.recommendationReasonList
54+
where r.member.id = :memberId
55+
""")
56+
List<Recommendation> findAllWithReasonsByMemberId(@Param("memberId") Long memberId);
4957
}

src/main/java/org/umc/valuedi/domain/savings/repository/SavingsOptionRepository.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,38 @@ List<SavingsOption> findAllByIdInFetchSavings(
2929
List<SavingsOption> findCandidates(
3030
Pageable pageable
3131
);
32+
33+
// G형(공격): 우대금리 우선 정렬
34+
@Query("""
35+
select so
36+
from SavingsOption so
37+
join fetch so.savings s
38+
where (:rsrvType is null or so.rsrvType = :rsrvType)
39+
and (:minTrm is null or so.saveTrm >= :minTrm)
40+
and (:maxTrm is null or so.saveTrm <= :maxTrm)
41+
order by so.intrRate2 desc nulls last, so.intrRate desc nulls last
42+
""")
43+
List<SavingsOption> findCandidatesOrderByRate2(
44+
@Param("rsrvType") String rsrvType,
45+
@Param("minTrm") Integer minTrm,
46+
@Param("maxTrm") Integer maxTrm,
47+
Pageable pageable
48+
);
49+
50+
// C형(보수): 기본금리 우선 정렬
51+
@Query("""
52+
select so
53+
from SavingsOption so
54+
join fetch so.savings s
55+
where (:rsrvType is null or so.rsrvType = :rsrvType)
56+
and (:minTrm is null or so.saveTrm >= :minTrm)
57+
and (:maxTrm is null or so.saveTrm <= :maxTrm)
58+
order by so.intrRate desc nulls last, so.intrRate2 desc nulls last
59+
""")
60+
List<SavingsOption> findCandidatesOrderByRate(
61+
@Param("rsrvType") String rsrvType,
62+
@Param("minTrm") Integer minTrm,
63+
@Param("maxTrm") Integer maxTrm,
64+
Pageable pageable
65+
);
3266
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.umc.valuedi.domain.savings.service;
2+
3+
import org.umc.valuedi.domain.mbti.enums.MbtiType;
4+
5+
/**
6+
* MBTI 타입을 추천 후보 쿼리 파라미터로 변환하는 값 객체.
7+
*
8+
* 축별 매핑:
9+
* - I(충동) → saveTrm <= 12 / P(계획) → saveTrm >= 24
10+
* - G(공격) → intrRate2 우선 정렬 / C(보수) → intrRate 우선 정렬
11+
* - V(회피) → rsrvType = 'S'(정기적립) / R(합리) → 무관
12+
* - A/S 축은 I/P 축에 흡수
13+
*/
14+
public record MbtiCandidateFilter(
15+
String rsrvType, // null=무관, "S"=정기적립
16+
Integer minTrm, // null=무관, 24=24개월 이상
17+
Integer maxTrm, // null=무관, 12=12개월 이하
18+
boolean orderByRate2First // true=우대금리순, false=기본금리순
19+
) {
20+
public static MbtiCandidateFilter from(MbtiType mbtiType) {
21+
String name = mbtiType.name(); // e.g. "APGV"
22+
char axis2 = name.charAt(1); // I or P
23+
char axis3 = name.charAt(2); // G or C
24+
char axis4 = name.charAt(3); // V or R
25+
26+
String rsrvType = (axis4 == 'V') ? "S" : null;
27+
Integer minTrm = (axis2 == 'P') ? 24 : null;
28+
Integer maxTrm = (axis2 == 'I') ? 12 : null;
29+
boolean orderByRate2 = (axis3 == 'G');
30+
31+
return new MbtiCandidateFilter(rsrvType, minTrm, maxTrm, orderByRate2);
32+
}
33+
}

src/main/java/org/umc/valuedi/domain/savings/service/RecommendationService.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,22 @@ public SavingsResponseDTO.SavingsListResponse generateAndSaveRecommendations(Lon
5757
MemberMbtiTest memberMbtiTest = memberMbtiTestRepository.findCurrentActiveTest(memberId)
5858
.orElseThrow(() -> new MbtiException(MbtiErrorCode.TYPE_INFO_NOT_FOUND));
5959

60-
// 추천 상품 후보 조회
60+
// 추천 상품 후보 조회: MBTI 타입 기반 필터링
61+
MbtiType mbtiType = memberMbtiTest.getResultType();
62+
MbtiCandidateFilter filter = MbtiCandidateFilter.from(mbtiType);
6163
Pageable candidatePage = PageRequest.of(0, CANDIDATE_LIMIT);
62-
List<SavingsOption> candidates = savingsOptionRepository.findCandidates(candidatePage);
6364

64-
// 제미나이 프롬프트 생성
65-
MbtiType mbtiType = memberMbtiTest.getResultType();
65+
List<SavingsOption> candidates = filter.orderByRate2First()
66+
? savingsOptionRepository.findCandidatesOrderByRate2(
67+
filter.rsrvType(), filter.minTrm(), filter.maxTrm(), candidatePage)
68+
: savingsOptionRepository.findCandidatesOrderByRate(
69+
filter.rsrvType(), filter.minTrm(), filter.maxTrm(), candidatePage);
70+
71+
// Fallback: 필터 결과가 부족하면 기본 후보로 대체
72+
if (candidates.size() < RECOMMEND_COUNT) {
73+
log.warn("[Recommend] 필터 후보 부족 (size={}), 기본 후보로 대체. mbtiType={}", candidates.size(), mbtiType);
74+
candidates = savingsOptionRepository.findCandidates(candidatePage);
75+
}
6676
String prompt = buildPrompt(mbtiType, candidates, RECOMMEND_COUNT);
6777

6878
// 제미나이 호출

src/main/java/org/umc/valuedi/domain/savings/service/RecommendationTxService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ public SavingsResponseDTO.RecommendResponse saveRecommendations(
5353
Map<Long, SavingsOption> optionById = pickedOptions.stream()
5454
.collect(Collectors.toMap(SavingsOption::getId, Function.identity()));
5555

56-
// 기존 추천 삭제 (중복 방지)
57-
List<Recommendation> existing = recommendationRepository.findAllByMemberIdAndMemberMbtiTestId(memberId, memberMbtiTest.getId());
56+
// 기존 추천 전체 삭제 - mbtiTestId가 달라져도 이전 추천이 누적되지 않도록 memberId 기준으로 삭제
57+
List<Recommendation> existing = recommendationRepository.findAllWithReasonsByMemberId(memberId);
5858
if (!existing.isEmpty()) {
5959
recommendationRepository.deleteAll(existing);
6060
}

0 commit comments

Comments
 (0)