4545import com .example .RealMatch .match .presentation .dto .response .MatchResponseDto ;
4646import com .example .RealMatch .match .presentation .dto .response .MatchResponseDto .BrandDto ;
4747import 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 ;
4852import com .example .RealMatch .user .domain .entity .User ;
4953import com .example .RealMatch .user .domain .entity .UserMatchingDetail ;
5054import 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}
0 commit comments