diff --git a/src/main/java/org/sopt/app/application/poke/FriendService.java b/src/main/java/org/sopt/app/application/poke/FriendService.java index 85fec921..a3c60e6c 100644 --- a/src/main/java/org/sopt/app/application/poke/FriendService.java +++ b/src/main/java/org/sopt/app/application/poke/FriendService.java @@ -20,8 +20,11 @@ public class FriendService { private final FriendRepository friendRepository; - public List findAllFriendIdsByUserIdRandomly(Long userId, int limitNum) { - return friendRepository.getFriendRandom(userId, limitNum); + public List findAllFriendIdsByUserIdRandomly(Long userId, List excludeUserIds, int limitNum) { + return friendRepository.getFriendRandom(userId, excludeUserIds, limitNum); + } + public List findAllFriendIdsByUserIdRandomlyIncludeDuplicatedFriend(Long userId, List excludeUserIds, int limitNum) { + return friendRepository.getFriendRandomIncludeDuplicated(userId, excludeUserIds, limitNum); } public List findAllFriendsByFriendship(Long userId, Integer lowerLimit, Integer upperLimit) { diff --git a/src/main/java/org/sopt/app/application/poke/PokeMessageService.java b/src/main/java/org/sopt/app/application/poke/PokeMessageService.java index 1ff7c5f1..7cdb56b9 100644 --- a/src/main/java/org/sopt/app/application/poke/PokeMessageService.java +++ b/src/main/java/org/sopt/app/application/poke/PokeMessageService.java @@ -14,8 +14,17 @@ public class PokeMessageService { private static final int MESSAGES_QUANTITY_AT_ONCE = 5; + private static final String MESSAGES_HEADER_FOR_POKE = "함께 보낼 메시지를 선택해주세요"; + private static final String MESSAGES_HEADER_FOR_REPLY = "답장하고 싶은 메시지를 선택해주세요"; private final PokeMessageRepository messageRepository; + public String getMessagesHeaderComment(String type) { + PokeMessageType pokeMessageType = PokeMessageType.ofParam(type); + if (pokeMessageType.equals(PokeMessageType.REPLY_NEW)) { + return MESSAGES_HEADER_FOR_REPLY; + } + return MESSAGES_HEADER_FOR_POKE; + } public List pickRandomMessageByTypeOf(String type) { PokeMessageType messageType = PokeMessageType.ofParam(type); val messages = messageRepository.findAllByType(messageType); diff --git a/src/main/java/org/sopt/app/facade/PokeFacade.java b/src/main/java/org/sopt/app/facade/PokeFacade.java index 46a15f40..afd53115 100644 --- a/src/main/java/org/sopt/app/facade/PokeFacade.java +++ b/src/main/java/org/sopt/app/facade/PokeFacade.java @@ -25,7 +25,9 @@ @Service @RequiredArgsConstructor public class PokeFacade { - + private static final String NEW_FRIEND_NO_MUTUAL = "새로운 친구"; + private static final String NEW_FRIEND_ONE_MUTUAL = "%s의 친구"; + private static final String NEW_FRIEND_MANY_MUTUAL = "%s 외 %d명과 친구"; private final PlaygroundAuthService playgroundAuthService; private final UserService userService; private final FriendService friendService; @@ -43,6 +45,9 @@ public List getPokingMessages(String type) { ) .toList(); } + public String getPokingMessageHeader(String type) { + return pokeMessageService.getMessagesHeaderComment(type); + } @Transactional(readOnly = true) public List getRecommendUserForNew(String playgroundToken, Long userPlaygroundId) { @@ -76,7 +81,7 @@ private List makeDummySimplePokeProfile(List use part, 0, Friendship.NON_FRIEND.getFriendshipName(), - List.of(), + NEW_FRIEND_NO_MUTUAL, true, false ); @@ -95,7 +100,12 @@ private List pickRandomUserIds( @Transactional(readOnly = true) public List getRecommendFriendsOfUsersFriend(User user) { - val friendsUserIds = friendService.findAllFriendIdsByUserIdRandomly(user.getId(), 2); + val hasPokeMeBeforeUserIds = pokeHistoryService.getPokeFriendIds(user.getId()); + List friendsUserIds = friendService.findAllFriendIdsByUserIdRandomly(user.getId(), hasPokeMeBeforeUserIds, 2); + if (friendsUserIds.isEmpty()) { + friendsUserIds = friendService.findAllFriendIdsByUserIdRandomlyIncludeDuplicatedFriend( + user.getId(), hasPokeMeBeforeUserIds, 2); + } return friendsUserIds.stream().map( friendsUserId -> { val friendUser = userService.getUserProfile(friendsUserId); @@ -188,7 +198,6 @@ public List getFriend(User user) { val pokeHistory = pokeHistoryService.getAllOfPokeBetween(user.getId(), friendId.get(0)).get(0); val isAlreadyPoke = pokeHistory.getPokerId().equals(user.getId()); - return List.of( SimplePokeProfile.of( friendUserProfile.get(0).getUserId(), @@ -200,7 +209,9 @@ public List getFriend(User user) { friendProfile.get(0).getActivities().get(0).getPart(), friendRelationInfo.getPokeCount(), friendRelationInfo.getRelationName(), - mutualFriendNames, + mutualFriendNames.size() == 0 ? NEW_FRIEND_NO_MUTUAL : + mutualFriendNames.size() == 1 ? String.format(NEW_FRIEND_ONE_MUTUAL, mutualFriendNames.get(0)) + : String.format(NEW_FRIEND_MANY_MUTUAL, mutualFriendNames.get(0), mutualFriendNames.size()-1), false, isAlreadyPoke ) @@ -261,6 +272,25 @@ public SimplePokeProfile getPokeHistoryProfile(User user, Long friendId, Long po PokeInfo.PokedUserInfo friendUserInfo = getFriendUserInfo( user, friendId); + List mutualFriendNames = friendUserInfo.getMutualFriendNames(); + if (friendUserInfo.getRelation().getPokeCount() == 0) { + return SimplePokeProfile.of( + friendUserInfo.getUserId(), + friendUserInfo.getPlaygroundId(), + friendUserInfo.getProfileImage() == null ? "" : friendUserInfo.getProfileImage(), + friendUserInfo.getName(), + pokeDetail.getMessage(), + friendUserInfo.getGeneration(), + friendUserInfo.getPart(), + 0, + friendUserInfo.getRelation().getRelationName(), + mutualFriendNames.size() == 0 ? NEW_FRIEND_NO_MUTUAL : + mutualFriendNames.size() == 1 ? String.format(NEW_FRIEND_ONE_MUTUAL, mutualFriendNames.get(0)) + : String.format(NEW_FRIEND_MANY_MUTUAL, mutualFriendNames.get(0), mutualFriendNames.size()-1), + true, + isAlreadyReply + ); + } return SimplePokeProfile.of( friendUserInfo.getUserId(), friendUserInfo.getPlaygroundId(), @@ -271,8 +301,8 @@ public SimplePokeProfile getPokeHistoryProfile(User user, Long friendId, Long po friendUserInfo.getPart(), friendUserInfo.getRelation().getPokeCount(), friendUserInfo.getRelation().getRelationName(), - friendUserInfo.getMutualFriendNames(), - friendUserInfo.getRelation().getPokeCount() == 0, + friendUserInfo.getRelation().getRelationName(), + false, isAlreadyReply ); } diff --git a/src/main/java/org/sopt/app/interfaces/postgres/FriendCustomRepository.java b/src/main/java/org/sopt/app/interfaces/postgres/FriendCustomRepository.java index 75aab082..dc3be0bd 100644 --- a/src/main/java/org/sopt/app/interfaces/postgres/FriendCustomRepository.java +++ b/src/main/java/org/sopt/app/interfaces/postgres/FriendCustomRepository.java @@ -1,9 +1,10 @@ package org.sopt.app.interfaces.postgres; import java.util.List; -import org.sopt.app.domain.entity.Friend; public interface FriendCustomRepository { - List getFriendRandom(Long userId, int limitNum); + List getFriendRandom(Long userId, List excludeUserIds, int limitNum); + + List getFriendRandomIncludeDuplicated(Long userId, List excludeUserIds, int limitNum); } diff --git a/src/main/java/org/sopt/app/interfaces/postgres/FriendRepositoryImpl.java b/src/main/java/org/sopt/app/interfaces/postgres/FriendRepositoryImpl.java index 371e1054..3ac5187f 100644 --- a/src/main/java/org/sopt/app/interfaces/postgres/FriendRepositoryImpl.java +++ b/src/main/java/org/sopt/app/interfaces/postgres/FriendRepositoryImpl.java @@ -5,7 +5,6 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; import lombok.RequiredArgsConstructor; -import org.sopt.app.domain.entity.Friend; import org.sopt.app.domain.entity.QFriend; @RequiredArgsConstructor @@ -13,28 +12,47 @@ public class FriendRepositoryImpl implements FriendCustomRepository { private final JPAQueryFactory queryFactory; - public List getFriendRandom(Long userId, int limitNum) { + public List getFriendRandom(Long userId, List excludeUserIds, int limitNum) { QFriend main = new QFriend("main"); QFriend sub = new QFriend("sub"); - QFriend f = new QFriend("f"); - - SubQueryExpression excludedFriendIds = - queryFactory - .select(f.friendUserId) - .from(f) - .where(f.userId.eq(userId)); - + SubQueryExpression queryOfExcludeIds = getQueryOfExcludeIds(userId); return queryFactory .select(sub.userId) .from(main) .join(sub).on(main.friendUserId.eq(sub.userId)) .where(main.userId.eq(userId) .and(sub.friendUserId.ne(userId)) - .and(sub.friendUserId.notIn(excludedFriendIds))) // Exclude the friend IDs + .and(sub.friendUserId.notIn(queryOfExcludeIds)) // Exclude the friend IDs + .and(sub.friendUserId.notIn(excludeUserIds))) // Exclude friend who user have to reply first IDs .groupBy(sub.userId) .having(sub.userId.count().goe(1)) .orderBy(Expressions.numberTemplate(Long.class, "RANDOM()").asc()) .limit(2) .fetch(); } + + public List getFriendRandomIncludeDuplicated(Long userId, List excludeUserIds, int limitNum) { + QFriend main = new QFriend("main"); + QFriend sub = new QFriend("sub"); + return queryFactory + .select(sub.userId) + .from(main) + .join(sub).on(main.friendUserId.eq(sub.userId)) + .where(main.userId.eq(userId) + .and(sub.friendUserId.ne(userId)) + .and(sub.friendUserId.notIn(excludeUserIds))) // Exclude friend who user have to reply first IDs + .groupBy(sub.userId) + .having(sub.userId.count().goe(1)) + .orderBy(Expressions.numberTemplate(Long.class, "RANDOM()").asc()) + .limit(2) + .fetch(); + } + + private SubQueryExpression getQueryOfExcludeIds(Long userId) { + QFriend f = new QFriend("f"); + return queryFactory + .select(f.friendUserId) + .from(f) + .where(f.userId.eq(userId)); + } } diff --git a/src/main/java/org/sopt/app/interfaces/postgres/PokeHistoryRepository.java b/src/main/java/org/sopt/app/interfaces/postgres/PokeHistoryRepository.java index 639be811..5c683017 100644 --- a/src/main/java/org/sopt/app/interfaces/postgres/PokeHistoryRepository.java +++ b/src/main/java/org/sopt/app/interfaces/postgres/PokeHistoryRepository.java @@ -23,8 +23,6 @@ public interface PokeHistoryRepository extends JpaRepository List findAllByPokedIdAndIsReplyIsFalseOrderByCreatedAtDesc(Long pokedId); Page findAllByPokedIdAndIsReplyIsFalseOrderByCreatedAtDesc(Long pokedId, Pageable pageable); - List findAllByPokerIdAndPokedIdOrderByCreatedAtDesc(Long pokerId, Long pokedId); - @Query("SELECT ph FROM PokeHistory ph WHERE ((ph.pokerId = :userId AND ph.pokedId = :friendId) OR (ph.pokerId = :friendId AND ph.pokedId = :userId)) AND ph.isReply = false ORDER BY ph.createdAt DESC ") List findAllWithFriendOrderByCreatedAtDesc(@Param("userId") Long userId, @Param("friendId") Long friendId); } \ No newline at end of file diff --git a/src/main/java/org/sopt/app/presentation/poke/PokeController.java b/src/main/java/org/sopt/app/presentation/poke/PokeController.java index 99eaa1f0..2d77eeb7 100644 --- a/src/main/java/org/sopt/app/presentation/poke/PokeController.java +++ b/src/main/java/org/sopt/app/presentation/poke/PokeController.java @@ -74,7 +74,8 @@ public ResponseEntity getPokeMessages( @RequestParam("messageType") String messageType ) { val messages = pokeFacade.getPokingMessages(messageType); - val response = PokeMessageList.of(messages); + val pokingMessageHeader = pokeFacade.getPokingMessageHeader(messageType); + val response = PokeMessageList.of(pokingMessageHeader, messages); return ResponseEntity.ok(response); } diff --git a/src/main/java/org/sopt/app/presentation/poke/PokeResponse.java b/src/main/java/org/sopt/app/presentation/poke/PokeResponse.java index d9138f14..fbf2b001 100644 --- a/src/main/java/org/sopt/app/presentation/poke/PokeResponse.java +++ b/src/main/java/org/sopt/app/presentation/poke/PokeResponse.java @@ -111,12 +111,14 @@ public static PokeToMeHistoryList of( @AllArgsConstructor(access = AccessLevel.PRIVATE) @ToString public static class PokeMessageList implements MessageList { + private String header; private List messages; public static PokeMessageList of( + String header, List messages ) { - return new PokeMessageList(messages); + return new PokeMessageList(header, messages); } } @@ -162,8 +164,13 @@ public static class SimplePokeProfile { private Integer pokeNum; @Schema(description = "관계 이름", example = "천생연분") private String relationName; - @Schema(description = "함께 친구 관계인 친구들의 이름", example = "['제갈송현', '왕건모', '진동규', '차승호']") - private List mutual; + + //TODO: 건모 Align 이후 삭제 +// @Schema(description = "함께 친구 관계인 친구들의 이름", example = "['제갈송현', '왕건모', '진동규', '차승호']") +// private List mutual; + @Schema(description = "함께 아는 친구관계 문구", example = "제갈송현 외 1명과 친구") + private String mutualRelationMessage; + @Schema(description = "이전에 찌른 이력이 있는지에 대한 여부", example = "false") private Boolean isFirstMeet; @Schema(description = "이미 오늘 찔렀는지에 대한 여부", example = "true") @@ -171,11 +178,16 @@ public static class SimplePokeProfile { public static SimplePokeProfile of( Long userId, Long playgroundId, String profileImage, String name, String message, - Integer generation, String part, Integer pickNum, String relationName, List mutual, Boolean isFirstMeet, + Integer generation, String part, Integer pickNum, String relationName, +// , List mutual + String mutualRelationMessage, Boolean isFirstMeet, Boolean isAlreadyPoke ) { return new SimplePokeProfile( - userId, playgroundId, profileImage, name, message, generation, part, pickNum, relationName, mutual, isFirstMeet, + userId, playgroundId, profileImage, name, message, generation, part, pickNum, relationName +// , mutual + , mutualRelationMessage + , isFirstMeet, isAlreadyPoke ); }