Skip to content

Commit

Permalink
Merge pull request #44 from Team-UMC/feature/#29/friend-api
Browse files Browse the repository at this point in the history
[FEAT] 친구 관련 API 구현
  • Loading branch information
junseokkim authored Feb 4, 2024
2 parents 8ff84f4 + 4209e66 commit 822f682
Show file tree
Hide file tree
Showing 27 changed files with 805 additions and 164 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.umc.networkingService.domain.friend.controller;

import com.umc.networkingService.config.security.auth.CurrentMember;
import com.umc.networkingService.domain.friend.dto.response.FriendIdResponse;
import com.umc.networkingService.domain.friend.dto.response.FriendInquiryByStatusResponse;
import com.umc.networkingService.domain.friend.service.FriendService;
import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.global.common.base.BaseResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.core.parameters.P;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

@Tag(name = "친구 API", description = "친구 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/friends")
public class FriendController {

private final FriendService friendService;

@Operation(summary = "친구 추가 API", description = "새로운 친구를 추가하는 API입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공"),
@ApiResponse(responseCode = "MEMBER001", description = "존재하지 않는 멤버를 친구 추가한 경우 발생"),
@ApiResponse(responseCode = "FRIEND001", description = "이미 친구 관계일 경우 발생")
})
@Parameter(name = "memberId", in = ParameterIn.PATH, required = true, description = "친구 추가할 멤버 id입니다.")
@PostMapping("/{memberId}")
public BaseResponse<FriendIdResponse> createNewFriend(@CurrentMember Member member,
@PathVariable UUID memberId) {
return BaseResponse.onSuccess(friendService.createNewFriend(member, memberId));
}

@Operation(summary = "친구 삭제 API", description = "친구 관계를 삭제하는 API입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공"),
@ApiResponse(responseCode = "MEMBER001", description = "존재하지 않는 멤버를 친구 삭제한 경우 발생"),
@ApiResponse(responseCode = "FRIEND002", description = "친구 관계가 아닌데 삭제한 경우 발생")
})
@Parameter(name = "memberId", in = ParameterIn.PATH, required = true, description = "친구 삭제할 멤버 id입니다.")
@DeleteMapping("/{memberId}")
public BaseResponse<FriendIdResponse> deleteFriend(@CurrentMember Member member,
@PathVariable UUID memberId) {
return BaseResponse.onSuccess(friendService.deleteFriend(member, memberId));
}

@Operation(summary = "접속/비접속 중인 친구 목록 조회 API", description = "친구 관계인 멤버 중 접속 또는 비접속 중인 멤버 조회 API입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "성공")
})
@Parameters(value = {
@Parameter(name = "status", required = true, description = "접속 상태입니다."),
@Parameter(name = "pageable", hidden = true),
@Parameter(name = "size", required = true, description = "한 페이지에 포함되는 목록 개수입니다.")
})
@GetMapping
public BaseResponse<FriendInquiryByStatusResponse> inquiryFriendsByStatus(
@CurrentMember Member member,
@RequestParam boolean status,
@PageableDefault(page = 1, sort = "receiver.nickname", direction = Sort.Direction.ASC) Pageable pageable) {
return BaseResponse.onSuccess(friendService.inquiryFriendsByStatus(member, status, pageable));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.umc.networkingService.domain.friend.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.UUID;

@Getter
@AllArgsConstructor
public class FriendIdResponse {
private UUID friendId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.umc.networkingService.domain.friend.dto.response;

import com.umc.networkingService.domain.member.dto.SemesterPartInfo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.UUID;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FriendInquiryByStatusResponse {
private List<FriendInfo> friends;
private boolean hasNext;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class FriendInfo {
private UUID memberId;
private String nickname;
private String name;
private String profileImage;
private String universityName;
private List<String> campusPositions;
private List<String> centerPositions;
private List<SemesterPartInfo> semesterParts;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.umc.networkingService.domain.friend.mapper;

import com.umc.networkingService.domain.friend.dto.response.FriendInquiryByStatusResponse;
import com.umc.networkingService.domain.friend.entity.Friend;
import com.umc.networkingService.domain.member.dto.SemesterPartInfo;
import com.umc.networkingService.domain.member.entity.Member;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class FriendMapper {

public Friend toFriend(Member sender, Member receiver) {
return Friend.builder()
.sender(sender)
.receiver(receiver)
.build();
}

public FriendInquiryByStatusResponse.FriendInfo toFriendInfo(Member member,
List<String> campusPositions,
List<String> centerPositions,
List<SemesterPartInfo> semesterParts) {
return FriendInquiryByStatusResponse.FriendInfo.builder()
.memberId(member.getId())
.nickname(member.getNickname())
.name(member.getName())
.profileImage(member.getProfileImage())
.universityName(member.getUniversity().getName())
.campusPositions(campusPositions)
.centerPositions(centerPositions)
.semesterParts(semesterParts)
.build();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,25 @@

import com.umc.networkingService.domain.friend.entity.Friend;
import com.umc.networkingService.domain.member.entity.Member;
import io.lettuce.core.dynamic.annotation.Param;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.time.LocalDateTime;
import java.util.Optional;
import java.util.UUID;

public interface FriendRepository extends JpaRepository<Friend, UUID> {
Optional<Friend> findBySenderAndReceiver(Member sender, Member receiver);
boolean existsBySenderAndReceiver(Member sender, Member receiver);

@Query("SELECT f.receiver FROM Friend f WHERE f.sender = :member AND " +
"(:status = true AND f.receiver.lastActiveTime >= :fiveMinutesAgo OR " +
":status = false AND f.receiver.lastActiveTime < :fiveMinutesAgo)")
Page<Member> findFriendsByStatus(@Param("member") Member member,
@Param("status") boolean status,
@Param("fiveMinutesAgo") LocalDateTime fiveMinutesAgo,
Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package com.umc.networkingService.domain.friend.service;

import com.umc.networkingService.domain.friend.dto.response.FriendIdResponse;
import com.umc.networkingService.domain.friend.dto.response.FriendInquiryByStatusResponse;
import com.umc.networkingService.domain.member.entity.Member;
import org.springframework.data.domain.Pageable;

import java.util.UUID;

public interface FriendService {
FriendIdResponse createNewFriend(Member member, UUID memberId);
FriendIdResponse deleteFriend(Member member, UUID memberId);
FriendInquiryByStatusResponse inquiryFriendsByStatus(Member member, boolean status, Pageable pageable);
boolean checkFriend(Member sender, Member receiver);
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,91 @@
package com.umc.networkingService.domain.friend.service;

import com.umc.networkingService.domain.friend.dto.response.FriendIdResponse;
import com.umc.networkingService.domain.friend.dto.response.FriendInquiryByStatusResponse;
import com.umc.networkingService.domain.friend.entity.Friend;
import com.umc.networkingService.domain.friend.mapper.FriendMapper;
import com.umc.networkingService.domain.friend.repository.FriendRepository;
import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.domain.member.entity.PositionType;
import com.umc.networkingService.domain.member.mapper.MemberMapper;
import com.umc.networkingService.domain.member.service.MemberService;
import com.umc.networkingService.global.common.exception.ErrorCode;
import com.umc.networkingService.global.common.exception.RestApiException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class FriendServiceImpl implements FriendService {

private final FriendRepository friendRepository;
private final FriendMapper friendMapper;
private final MemberMapper memberMapper;

private final MemberService memberService;

// 친구 추가 함수
@Override
public FriendIdResponse createNewFriend(Member member, UUID memberId) {
Member loginMember = memberService.loadEntity(member.getId());

Member friendMember = memberService.loadEntity(memberId);

if (friendRepository.existsBySenderAndReceiver(loginMember, friendMember))
throw new RestApiException(ErrorCode.ALREADY_FRIEND_RELATION);

return createAndSaveNewFriend(loginMember, friendMember);
}

// 친구 삭제 함수
@Override
public FriendIdResponse deleteFriend(Member member, UUID memberId) {
Member loginMember = memberService.loadEntity(member.getId());

Member friendMember = memberService.loadEntity(memberId);

// 존재하지 않을 경우 예외 처리
Friend friend = friendRepository.findBySenderAndReceiver(loginMember, friendMember)
.orElseThrow(() -> new RestApiException(ErrorCode.NOT_FRIEND_RELATION));

friend.delete();

return new FriendIdResponse(friend.getId());
}

@Override
public FriendInquiryByStatusResponse inquiryFriendsByStatus(Member member, boolean status, Pageable pageable) {
LocalDateTime fiveMinutesAgo = LocalDateTime.now().minusMinutes(5);
Page<Member> friends = friendRepository.findFriendsByStatus(member, status, fiveMinutesAgo, pageable);

List<FriendInquiryByStatusResponse.FriendInfo> friendInfos = friends.stream()
.map(friend -> friendMapper.toFriendInfo(
friend,
memberService.getPositionNamesByType(friend, PositionType.CAMPUS),
memberService.getPositionNamesByType(friend, PositionType.CENTER),
memberMapper.toSemesterPartInfos(friend.getSemesterParts())
)).toList();


return new FriendInquiryByStatusResponse(friendInfos, friends.hasNext());
}

// 친구 여부 확인 함수
@Override
public boolean checkFriend(Member sender, Member receiver) {
return friendRepository.existsBySenderAndReceiver(sender, receiver);
}

private FriendIdResponse createAndSaveNewFriend(Member loginMember, Member friendMember) {
Friend friend = friendMapper.toFriend(loginMember, friendMember);
UUID savedFriendId = friendRepository.save(friend).getId();

return new FriendIdResponse(savedFriendId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.umc.networkingService.domain.friend.service;


import com.umc.networkingService.domain.member.dto.response.MemberInquiryProfileResponse;
import com.umc.networkingService.domain.member.entity.Member;

import java.util.UUID;

public interface FriendShipService {
MemberInquiryProfileResponse inquiryProfile(Member member, UUID memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.umc.networkingService.domain.friend.service;

import com.umc.networkingService.domain.friend.repository.FriendRepository;
import com.umc.networkingService.domain.member.dto.response.MemberInquiryProfileResponse;
import com.umc.networkingService.domain.member.entity.Member;
import com.umc.networkingService.domain.member.entity.MemberRelation;
import com.umc.networkingService.domain.member.mapper.MemberMapper;
import com.umc.networkingService.domain.member.repository.MemberRepository;
import com.umc.networkingService.domain.member.service.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
@RequiredArgsConstructor
public class FriendShipServiceImpl implements FriendShipService{
private final FriendService friendService;
private final MemberService memberService;

private final MemberMapper memberMapper;

// 프로필 조회 함수
@Override
public MemberInquiryProfileResponse inquiryProfile(Member member, UUID memberId) {

Member loginMember = memberService.loadEntity(member.getId());
// 본인 프로필 조회인 경우
if (memberId == null || loginMember.getId().equals(memberId)) {
return memberMapper.toInquiryProfileResponse(loginMember, MemberRelation.MINE);
}

Member inquiryMember = memberService.loadEntity(memberId);

// 친구 프로필 조회인 경우
if (friendService.checkFriend(loginMember, inquiryMember)) {
return memberMapper.toInquiryProfileResponse(inquiryMember, MemberRelation.FRIEND);
}

// 이외의 프로필 조회인 경우
return memberMapper.toInquiryProfileResponse(inquiryMember, MemberRelation.OTHERS);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
public class AuthController {
private final AuthService authService;

@Operation(summary = "소셜 로그인", description = "네이버, 카카오, 구글, 애플 로그인")
@Operation(summary = "소셜 로그인 API", description = "네이버, 카카오, 구글 로그인을 수행하는 API입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "COMMON200", description = "로그인 성공"),
@ApiResponse(responseCode = "AUTH007", description = "외부 소셜 서버와의 통신 에러" , content =
Expand Down
Loading

0 comments on commit 822f682

Please sign in to comment.