From 98342708c7e15e9757a2c7bdfef243ff3e85f4ef Mon Sep 17 00:00:00 2001 From: projectmiluju Date: Tue, 16 Sep 2025 04:03:06 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor(ProfileSearchService):=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EA=B2=80=EC=83=89=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=98=A4=EB=A6=84=EC=B0=A8=EC=88=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=20DP-430?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/repository/UserRepository.java | 25 +++++ .../player/service/ProfileSearchService.java | 102 ++---------------- 2 files changed, 32 insertions(+), 95 deletions(-) diff --git a/src/main/java/goorm/ddok/member/repository/UserRepository.java b/src/main/java/goorm/ddok/member/repository/UserRepository.java index 310561f2..44605dc1 100644 --- a/src/main/java/goorm/ddok/member/repository/UserRepository.java +++ b/src/main/java/goorm/ddok/member/repository/UserRepository.java @@ -1,6 +1,8 @@ package goorm.ddok.member.repository; import goorm.ddok.member.domain.UserLocation; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import goorm.ddok.member.domain.User; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -202,4 +204,27 @@ interface UserOverlayRow { AND u.id = :id """, nativeQuery = true) Optional findOverlayById(@Param("id") Long id); + + @Query(""" + SELECT DISTINCT u FROM User u + LEFT JOIN FETCH u.location loc + LEFT JOIN u.positions pos + WHERE ( + :keyword IS NULL OR :keyword = '' OR + LOWER(u.nickname) LIKE LOWER(CONCAT('%', :keyword, '%')) OR + (LOWER(pos.positionName) LIKE LOWER(CONCAT('%', :keyword, '%'))) OR + (LOWER(COALESCE(loc.region1DepthName, '')) LIKE LOWER(CONCAT('%', :keyword, '%'))) OR + (LOWER(COALESCE(loc.region2DepthName, '')) LIKE LOWER(CONCAT('%', :keyword, '%'))) OR + (LOWER(COALESCE(loc.region3DepthName, '')) LIKE LOWER(CONCAT('%', :keyword, '%'))) OR + (LOWER(COALESCE(loc.roadName, '')) LIKE LOWER(CONCAT('%', :keyword, '%'))) OR + (LOWER(COALESCE(loc.mainBuildingNo, '')) LIKE LOWER(CONCAT('%', :keyword, '%'))) OR + (LOWER(COALESCE(loc.subBuildingNo, '')) LIKE LOWER(CONCAT('%', :keyword, '%'))) OR + (LOWER(CONCAT(COALESCE(loc.region1DepthName, ''), ' ', COALESCE(loc.region2DepthName, ''))) LIKE LOWER(CONCAT('%', :keyword, '%'))) + ) + AND ( + (:keyword IS NOT NULL AND :keyword != '') + ) + ORDER BY LOWER(u.nickname) ASC, u.id ASC + """) + Page searchPlayersWithKeyword(@Param("keyword") String keyword, Pageable pageable); } diff --git a/src/main/java/goorm/ddok/player/service/ProfileSearchService.java b/src/main/java/goorm/ddok/player/service/ProfileSearchService.java index 8e31398b..fa6473d9 100644 --- a/src/main/java/goorm/ddok/player/service/ProfileSearchService.java +++ b/src/main/java/goorm/ddok/player/service/ProfileSearchService.java @@ -43,109 +43,23 @@ public Page searchPlayers(String keyword, int page, int s page = Math.max(page, 0); size = (size <= 0) ? 10 : size; - // Sort 제거하고 Pageable만 생성 Pageable pageable = PageRequest.of(page, size); - Specification spec = hasText(keyword) - ? keywordSpecWithSort(keyword) - : publicUsersWithSort(); + String searchKeyword = hasText(keyword) ? keyword.trim() : null; - Page rows = userRepository.findAll(spec, pageable); + Page rows = userRepository.searchPlayersWithKeyword(searchKeyword, pageable); return rows.map(u -> toResponse(u, currentUserId)); } - private Specification keywordSpecWithSort(String raw) { - List tokens = splitTokens(raw); - - return (root, query, cb) -> { - // 기존 검색 로직 - Join locJoin = root.join("location", JoinType.LEFT); - - List andPerToken = new ArrayList<>(); - for (String token : tokens) { - String like = "%" + token.toLowerCase() + "%"; - List ors = new ArrayList<>(); - - ors.add(cb.like(cb.lower(root.get("nickname")), like)); - - Subquery posSub = Objects.requireNonNull(query).subquery(Long.class); - Root p = posSub.from(UserPosition.class); - posSub.select(cb.literal(1L)); - posSub.where( - cb.equal(p.get("user").get("id"), root.get("id")), - cb.like(cb.lower(p.get("positionName")), like) - ); - ors.add(cb.and(cb.isTrue(root.get("isPublic")), cb.exists(posSub))); - - ors.add(cb.and(cb.isTrue(root.get("isPublic")), - cb.like(cb.lower(cb.coalesce(locJoin.get("region1DepthName"), "")), like))); - ors.add(cb.and(cb.isTrue(root.get("isPublic")), - cb.like(cb.lower(cb.coalesce(locJoin.get("region2DepthName"), "")), like))); - ors.add(cb.and(cb.isTrue(root.get("isPublic")), - cb.like(cb.lower(cb.coalesce(locJoin.get("region3DepthName"), "")), like))); - ors.add(cb.and(cb.isTrue(root.get("isPublic")), - cb.like(cb.lower(cb.coalesce(locJoin.get("roadName"), "")), like))); - ors.add(cb.and(cb.isTrue(root.get("isPublic")), - cb.like(cb.lower(cb.coalesce(locJoin.get("mainBuildingNo"), "")), like))); - ors.add(cb.and(cb.isTrue(root.get("isPublic")), - cb.like(cb.lower(cb.coalesce(locJoin.get("subBuildingNo"), "")), like))); - - ors.add(cb.and(cb.isTrue(root.get("isPublic")), - cb.like( - cb.lower( - cb.concat( - cb.concat(cb.coalesce(locJoin.get("region1DepthName"), ""), " "), - cb.coalesce(locJoin.get("region2DepthName"), "") - ) - ), - like - ))); - - andPerToken.add(cb.or(ors.toArray(new Predicate[0]))); - } - - // 정렬 추가 - if (query != null) { - query.orderBy( - cb.asc(root.get("nickname")), - cb.asc(root.get("id")) - ); - } - - return cb.and(andPerToken.toArray(new Predicate[0])); - }; - } - - private Specification publicUsersWithSort() { - return (root, query, cb) -> { - // 정렬 추가 - if (query != null) { - query.orderBy( - cb.asc(root.get("nickname")), - cb.asc(root.get("id")) - ); - } - return cb.isTrue(root.get("isPublic")); - }; - } - - private List splitTokens(String raw) { - return Arrays.stream(raw.split("[,\\s]+")) - .map(String::trim) - .filter(t -> !t.isEmpty()) - .distinct() - .toList(); - } - + // 나머지 메서드들은 그대로 유지 private ProfileSearchResponse toResponse(User u, Long currentUserId) { - + // 기존 코드 그대로 boolean isMine = Objects.equals(u.getId(), currentUserId); boolean isPublic = u.isPublic(); String address = null; String mainPosition = null; - // isPublic이 true이거나 본인인 경우에만 주소와 포지션 정보 제공 if (isPublic || isMine) { address = shortAddress( u.getLocation() == null ? null : u.getLocation().getRegion1DepthName(), @@ -153,11 +67,11 @@ private ProfileSearchResponse toResponse(User u, Long currentUserId) { ); mainPosition = pickMainPosition(u); } + BigDecimal temp = userReputationRepository.findByUserId(u.getId()) .map(UserReputation::getTemperature) .orElse(null); - // 대표 배지 조회 BadgeDto representative = badgeService.getRepresentativeGoodBadge(u); ProfileSearchResponse.MainBadge mainBadge = null; if (representative != null) { @@ -167,7 +81,6 @@ private ProfileSearchResponse toResponse(User u, Long currentUserId) { .build(); } - // 나쁜 배지 조회 AbandonBadgeDto abandon = badgeService.getAbandonBadge(u); ProfileSearchResponse.AbandonBadge abandonBadge = ProfileSearchResponse.AbandonBadge.builder() @@ -183,7 +96,6 @@ private ProfileSearchResponse toResponse(User u, Long currentUserId) { || dmRequestService.isDmPendingOrAcceptedOrChatExists(currentUserId, u.getId()); } - return ProfileSearchResponse.builder() .userId(u.getId()) .category("players") @@ -191,8 +103,8 @@ private ProfileSearchResponse toResponse(User u, Long currentUserId) { .profileImageUrl(u.getProfileImageUrl()) .mainBadge(mainBadge) .abandonBadge(abandonBadge) - .mainPosition(mainPosition) // isPublic이 false이면 null - .address(address) // isPublic이 false이면 null + .mainPosition(mainPosition) + .address(address) .temperature(temp) .IsMine(currentUserId != null && currentUserId.equals(u.getId())) .chatRoomId(chatRoomId) From ffa477b4d90f0e20f63253f15a0d3b553e8b820c Mon Sep 17 00:00:00 2001 From: projectmiluju Date: Tue, 16 Sep 2025 04:03:21 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(ProfileSearchService):=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EA=B2=80=EC=83=89=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=98=A4=EB=A6=84=EC=B0=A8=EC=88=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=20DP-430?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/goorm/ddok/player/service/ProfileSearchService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/goorm/ddok/player/service/ProfileSearchService.java b/src/main/java/goorm/ddok/player/service/ProfileSearchService.java index fa6473d9..2c6261c4 100644 --- a/src/main/java/goorm/ddok/player/service/ProfileSearchService.java +++ b/src/main/java/goorm/ddok/player/service/ProfileSearchService.java @@ -6,7 +6,6 @@ import goorm.ddok.global.dto.AbandonBadgeDto; import goorm.ddok.global.dto.BadgeDto; import goorm.ddok.member.domain.User; -import goorm.ddok.member.domain.UserLocation; import goorm.ddok.member.domain.UserPosition; import goorm.ddok.member.domain.UserPositionType; import goorm.ddok.member.repository.UserRepository; @@ -18,7 +17,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;