Skip to content

Commit 9a260b2

Browse files
authored
Merge pull request #308 from RealMatchTeam/refactor/307-editfeature
[REFACTOR] 내 특성조회 usertag기반 (#307)
2 parents 35ae30d + ea6c404 commit 9a260b2

File tree

5 files changed

+383
-352
lines changed

5 files changed

+383
-352
lines changed

src/main/java/com/example/RealMatch/match/application/service/MatchServiceImpl.java

Lines changed: 119 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
import com.example.RealMatch.match.presentation.dto.response.MatchResponseDto;
4646
import com.example.RealMatch.match.presentation.dto.response.MatchResponseDto.BrandDto;
4747
import com.example.RealMatch.match.presentation.dto.response.MatchResponseDto.HighMatchingBrandListDto;
48+
import com.example.RealMatch.tag.domain.entity.Tag;
49+
import com.example.RealMatch.tag.domain.entity.UserTag;
50+
import com.example.RealMatch.tag.domain.repository.TagRepository;
51+
import com.example.RealMatch.tag.domain.repository.UserTagRepository;
4852
import com.example.RealMatch.user.domain.entity.User;
4953
import com.example.RealMatch.user.domain.entity.UserMatchingDetail;
5054
import com.example.RealMatch.user.domain.repository.UserMatchingDetailRepository;
@@ -72,24 +76,28 @@ public class MatchServiceImpl implements MatchService {
7276
private final UserMatchingDetailRepository userMatchingDetailRepository;
7377
private final MatchBrandHistoryRepository matchBrandHistoryRepository;
7478
private final MatchCampaignHistoryRepository matchCampaignHistoryRepository;
79+
private final UserTagRepository userTagRepository;
80+
private final TagRepository tagRepository;
7581

7682
// 매칭 요청 //
7783

7884
/**
7985
* 매칭 검사는 다음을 하나의 트랜잭션으로 처리한다.
8086
* - 기존 UserMatchingDetail 폐기
81-
* - 새 UserMatchingDetail 생성
87+
* - 새 UserMatchingDetail 생성 (creatorType + snsUrl만)
88+
* - UserTag 전량 교체 저장 (나머지 정보 전부 user_tag로)
8289
* - 브랜드/캠페인 매칭 히스토리 갱신
8390
*/
8491
@Override
8592
@Transactional
8693
public MatchResponseDto match(Long userId, MatchRequestDto requestDto) {
94+
8795
UserTagDocument userDoc = convertToUserTagDocument(userId, requestDto);
8896

8997
String userType = determineUserType(userDoc);
9098
List<String> typeTag = determineTypeTags(userDoc);
9199

92-
replaceUserMatchingDetail(userId, requestDto, userType);
100+
replaceUserMatchingDetailAndTags(userId, requestDto, userType);
93101

94102
List<BrandMatchResult> brandResults = findMatchingBrandResults(userDoc, userId);
95103

@@ -124,6 +132,53 @@ public MatchResponseDto match(Long userId, MatchRequestDto requestDto) {
124132
.build();
125133
}
126134

135+
/**
136+
* 매칭검사 결과 저장 (트랜잭션 1개로 원자 처리)
137+
* - UserMatchingDetail(유저매칭결과): creatorType + snsUrl만 저장
138+
* - UserTag(유저태그): 그 외 태그 전부 저장
139+
*/
140+
private void replaceUserMatchingDetailAndTags(Long userId, MatchRequestDto dto, String creatorType) {
141+
142+
// A. 기존 Detail 폐기 및 새 Detail 생성 (creatorType + snsUrl만)
143+
userMatchingDetailRepository.findByUserIdAndIsDeprecatedFalse(userId)
144+
.ifPresent(UserMatchingDetail::deprecated);
145+
146+
String snsUrl = (dto.getContent() != null && dto.getContent().getSns() != null)
147+
? dto.getContent().getSns().getUrl()
148+
: null;
149+
150+
UserMatchingDetail newDetail = UserMatchingDetail.builder()
151+
.userId(userId)
152+
.snsUrl(snsUrl)
153+
.build();
154+
155+
// ✅ 프로젝트 엔티티 메서드명에 맞춰 사용 (현재 너 코드 기준: setMatchingResult)
156+
newDetail.setMatchingResult(creatorType);
157+
158+
userMatchingDetailRepository.save(newDetail);
159+
160+
// B. 기존 태그 삭제 및 새 태그 저장 (나머지 정보 전부 user_tag로)
161+
userTagRepository.deleteByUserId(userId);
162+
163+
Set<Integer> tagIds = collectAllTagIds(dto);
164+
if (tagIds.isEmpty()) {
165+
return;
166+
}
167+
168+
User userRef = userRepository.getReferenceById(userId); // DB조회 없이 프록시만 (성능)
169+
List<Tag> tags = tagRepository.findAllById(tagIds.stream().map(Long::valueOf).toList());
170+
171+
List<UserTag> userTags = tags.stream()
172+
.map(tag -> UserTag.builder()
173+
.user(userRef)
174+
.tag(tag)
175+
.isDeprecated(false)
176+
.build())
177+
.toList();
178+
179+
userTagRepository.saveAll(userTags);
180+
}
181+
127182
private void saveMatchHistory(Long userId, List<BrandMatchResult> brandResults, List<CampaignMatchResult> campaignResults) {
128183
User user = userRepository.findById(userId).orElse(null);
129184
if (user == null) {
@@ -375,7 +430,6 @@ private List<CampaignMatchResult> findMatchingCampaignResults(UserTagDocument us
375430
.toList();
376431
}
377432

378-
379433
private Map<Long, Long> getBrandLikeCountMap() {
380434
return brandLikeRepository.findAll().stream()
381435
.collect(Collectors.groupingBy(
@@ -577,12 +631,13 @@ private MatchCampaignResponseDto.CampaignDto toCampaignCardDto(
577631
: 0;
578632

579633
Set<Long> likedBrandIds = brandLikeRepository.findByUserId(history.getUser().getId()).stream()
580-
.map(like -> like.getBrand().getId())
581-
.collect(Collectors.toSet());
634+
.map(like -> like.getBrand().getId())
635+
.collect(Collectors.toSet());
582636

583637
boolean isRecruiting = campaign.getRecruitEndDate() == null
584638
|| campaign.getRecruitEndDate().isAfter(LocalDateTime.now());
585639
Integer matchRatio = history.getMatchingRatio() != null ? history.getMatchingRatio().intValue() : 0;
640+
586641
return MatchCampaignResponseDto.CampaignDto.builder()
587642
.brandId(brand != null ? brand.getId() : null)
588643
.brandName(brand != null ? brand.getBrandName() : null)
@@ -601,28 +656,6 @@ private MatchCampaignResponseDto.CampaignDto toCampaignCardDto(
601656
.build();
602657
}
603658

604-
/**
605-
* 사용자의 마지막 매칭 상세 정보를 저장합니다 By 고경수
606-
*/
607-
private void replaceUserMatchingDetail(Long userId, MatchRequestDto requestDto, String userType) {
608-
609-
// 1) 기존 활성 detail 폐기
610-
userMatchingDetailRepository.findByUserIdAndIsDeprecatedFalse(userId)
611-
.ifPresent(detail -> {
612-
detail.deprecated();
613-
userMatchingDetailRepository.save(detail); // 명시적으로
614-
});
615-
616-
// 2) 새 detail 생성 (requestDto → entity 매핑)
617-
UserMatchingDetail newDetail = UserMatchingDetail.from(userId, requestDto);
618-
619-
// 3) 결과 저장(원하면)
620-
newDetail.setMatchingResult(userType);
621-
622-
// 4) 저장
623-
userMatchingDetailRepository.save(newDetail);
624-
}
625-
626659
private record BrandMatchResult(
627660
BrandTagDocument brandDoc,
628661
int matchScore,
@@ -638,4 +671,63 @@ private record CampaignMatchResult(
638671
int currentApplyCount
639672
) {
640673
}
674+
675+
/**
676+
* “유저태그로 들어가야 하는 모든 태그”를 한 번에 수집
677+
* (creatorType/snsUrl 제외한 나머지 정보는 전부 user_tag로 저장)
678+
*/
679+
private Set<Integer> collectAllTagIds(MatchRequestDto dto) {
680+
Set<Integer> tagIds = new HashSet<>();
681+
682+
if (dto.getFashion() != null) {
683+
addAll(tagIds, dto.getFashion().getInterestStyleTags());
684+
addAll(tagIds, dto.getFashion().getPreferredItemTags());
685+
addAll(tagIds, dto.getFashion().getPreferredBrandTags());
686+
addOne(tagIds, dto.getFashion().getHeightTag());
687+
addOne(tagIds, dto.getFashion().getWeightTypeTag());
688+
addOne(tagIds, dto.getFashion().getTopSizeTag());
689+
addOne(tagIds, dto.getFashion().getBottomSizeTag());
690+
}
691+
692+
if (dto.getBeauty() != null) {
693+
addAll(tagIds, dto.getBeauty().getInterestStyleTags());
694+
addAll(tagIds, dto.getBeauty().getPrefferedFunctionTags());
695+
addOne(tagIds, dto.getBeauty().getSkinTypeTags());
696+
addOne(tagIds, dto.getBeauty().getSkinToneTags());
697+
addOne(tagIds, dto.getBeauty().getMakeupStyleTags());
698+
}
699+
700+
if (dto.getContent() != null) {
701+
addAll(tagIds, dto.getContent().getTypeTags());
702+
addAll(tagIds, dto.getContent().getToneTags());
703+
addAll(tagIds, dto.getContent().getPrefferedInvolvementTags());
704+
addAll(tagIds, dto.getContent().getPrefferedCoverageTags());
705+
706+
if (dto.getContent().getSns() != null) {
707+
var sns = dto.getContent().getSns();
708+
if (sns.getMainAudience() != null) {
709+
addAll(tagIds, sns.getMainAudience().getAgeTags());
710+
addAll(tagIds, sns.getMainAudience().getGenderTags());
711+
}
712+
if (sns.getAverageAudience() != null) {
713+
addAll(tagIds, sns.getAverageAudience().getVideoLengthTags());
714+
addAll(tagIds, sns.getAverageAudience().getVideoViewsTags());
715+
}
716+
}
717+
}
718+
719+
return tagIds;
720+
}
721+
722+
private void addAll(Set<Integer> set, List<Integer> values) {
723+
if (values != null) {
724+
set.addAll(values);
725+
}
726+
}
727+
728+
private void addOne(Set<Integer> set, Integer value) {
729+
if (value != null) {
730+
set.add(value);
731+
}
732+
}
641733
}

src/main/java/com/example/RealMatch/tag/domain/entity/UserTag.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.example.RealMatch.global.common.BaseEntity;
44
import com.example.RealMatch.user.domain.entity.User;
55

6+
import jakarta.persistence.Column;
67
import jakarta.persistence.Entity;
78
import jakarta.persistence.FetchType;
89
import jakarta.persistence.GeneratedValue;
@@ -40,12 +41,17 @@ public class UserTag extends BaseEntity {
4041
@JoinColumn(name = "tag_id", nullable = false)
4142
private Tag tag;
4243

44+
@Column(name = "is_deprecated", nullable = false)
45+
private boolean isDeprecated = false;
46+
4347
@Builder
4448
public UserTag(
4549
User user,
46-
Tag tag
50+
Tag tag,
51+
Boolean isDeprecated
4752
) {
4853
this.user = user;
4954
this.tag = tag;
55+
this.isDeprecated = (isDeprecated != null) ? isDeprecated : false;
5056
}
5157
}

src/main/java/com/example/RealMatch/tag/domain/repository/UserTagRepository.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
public interface UserTagRepository extends JpaRepository<UserTag, Long> {
1212

1313
@Query("""
14-
select ut
15-
from UserTag ut
16-
join fetch ut.tag t
17-
where ut.user.id = :userId
14+
select ut from UserTag ut
15+
join fetch ut.tag t
16+
where ut.user.id = :userId
17+
and ut.isDeprecated = false
1818
""")
1919
List<UserTag> findAllByUserIdWithTag(@Param("userId") Long userId);
2020

0 commit comments

Comments
 (0)