diff --git a/src/main/java/com/codiary/backend/global/converter/MemberConverter.java b/src/main/java/com/codiary/backend/global/converter/MemberConverter.java index 3e4c96d9..b7e40ef3 100644 --- a/src/main/java/com/codiary/backend/global/converter/MemberConverter.java +++ b/src/main/java/com/codiary/backend/global/converter/MemberConverter.java @@ -5,11 +5,11 @@ import com.codiary.backend.global.domain.entity.Member; import com.codiary.backend.global.domain.entity.Team; import com.codiary.backend.global.domain.entity.mapping.MemberCategory; -import com.codiary.backend.global.domain.entity.mapping.TeamMember; import com.codiary.backend.global.domain.entity.mapping.TechStacks; import com.codiary.backend.global.web.dto.Member.FollowResponseDto; import com.codiary.backend.global.web.dto.Member.MemberResponseDTO; import com.codiary.backend.global.web.dto.Member.MemberSumResponseDto; +import com.codiary.backend.global.web.dto.Team.TeamResponseDTO; import org.springframework.data.domain.Page; import org.springframework.stereotype.Component; @@ -174,8 +174,13 @@ public static MemberResponseDTO.UserProfileDTO toProfileResponseDto(Member membe .map(TechStacks::getName) .collect(Collectors.toList())) .teamList(user.getTeamMemberList().stream() - .map(TeamMember::getTeam) - .map(Team::getName) + .map(teamMember -> { + Team team = teamMember.getTeam(); + return TeamResponseDTO.TeamPreviewDTO.builder() + .teamId(team.getTeamId()) + .teamName(team.getName()) + .build(); + }) .collect(Collectors.toList())) .myPage(user.getMemberId().equals(member.getMemberId())) .build(); diff --git a/src/main/java/com/codiary/backend/global/service/MemberService/FollowService.java b/src/main/java/com/codiary/backend/global/service/MemberService/FollowService.java index 0096ebab..42449170 100644 --- a/src/main/java/com/codiary/backend/global/service/MemberService/FollowService.java +++ b/src/main/java/com/codiary/backend/global/service/MemberService/FollowService.java @@ -1,112 +1,14 @@ package com.codiary.backend.global.service.MemberService; -import com.codiary.backend.global.apiPayload.code.status.ErrorStatus; -import com.codiary.backend.global.apiPayload.exception.GeneralException; import com.codiary.backend.global.domain.entity.Follow; import com.codiary.backend.global.domain.entity.Member; -import com.codiary.backend.global.repository.FollowRepository; -import com.codiary.backend.global.repository.MemberRepository; - -import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; import java.util.List; -import java.util.stream.Collectors; - -@Service -@RequiredArgsConstructor -public class FollowService { - private final FollowRepository followRepository; - private final MemberRepository memberRepository; - - @Transactional - public Follow follow(Long toId, Member fromMember) { - if (fromMember == null) { - throw new GeneralException(ErrorStatus.MEMBER_NOT_FOUND); - } - if (toId.equals(fromMember.getMemberId())) { - throw new GeneralException(ErrorStatus.MEMBER_SELF_FOLLOW); - } - - fromMember = memberRepository.findByToIdWithFollowings(fromMember.getMemberId()) - .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); - Member toMember = memberRepository.findByToIdWithFollowers(toId) - .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); - - Follow follow = followRepository.findByFromMemberAndToMember(fromMember, toMember) - .orElse(null); - - if (follow == null) { - follow = Follow.builder() - .fromMember(fromMember) - .toMember(toMember) - .followStatus(true) - .build(); - fromMember.getFollowings().add(follow); - toMember.getFollowers().add(follow); - } else { - if (follow.getFollowStatus()) { - follow.update(false); - fromMember.getFollowings().remove(follow); - toMember.getFollowers().remove(follow); - } else { - follow.update(true); - fromMember.getFollowings().add(follow); - toMember.getFollowers().add(follow); - } - } - followRepository.save(follow); - - return follow; - } - - @Transactional - public Boolean isFollowing(Long toId, Member fromMember) { - if(fromMember == null){ - throw new GeneralException(ErrorStatus.MEMBER_NOT_FOUND); - } - if (toId.equals(fromMember.getMemberId())) { - throw new GeneralException(ErrorStatus.MEMBER_SELF_FOLLOW); - } - Member toMember = memberRepository.findById(toId) - .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); - - return followRepository.findByFromMemberAndToMember(fromMember, toMember) - .map(Follow::getFollowStatus) - .orElse(false); - } - - - @Transactional - public List getFollowings(Member member) { - if (member == null) { - throw new GeneralException(ErrorStatus.MEMBER_NOT_FOUND); - } - - member = memberRepository.findByToIdWithFollowings(member.getMemberId()) - .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); - - List followings = followRepository.findByFromMemberAndFollowStatusTrueOrderByUpdatedAt(member); - - return followings.stream() - .map(Follow::getToMember) - .collect(Collectors.toList()); - } - - @Transactional - public List getFollowers(Member member) { - if (member == null) { - throw new GeneralException(ErrorStatus.MEMBER_NOT_FOUND); - } - - member = memberRepository.findByToIdWithFollowers(member.getMemberId()) - .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); - List followers = followRepository.findByToMemberAndFollowStatusTrueOrderByUpdatedAt(member); +public interface FollowService { - return followers.stream() - .map(Follow::getFromMember) - .collect(Collectors.toList()); - } + Follow follow(Long toId, Member fromMember); + Boolean isFollowing(Long toId, Member fromMember); + List getFollowings(Member member); + List getFollowers(Member member); } diff --git a/src/main/java/com/codiary/backend/global/service/MemberService/FollowServiceImpl.java b/src/main/java/com/codiary/backend/global/service/MemberService/FollowServiceImpl.java new file mode 100644 index 00000000..41653e9f --- /dev/null +++ b/src/main/java/com/codiary/backend/global/service/MemberService/FollowServiceImpl.java @@ -0,0 +1,113 @@ +package com.codiary.backend.global.service.MemberService; + + +import com.codiary.backend.global.apiPayload.code.status.ErrorStatus; +import com.codiary.backend.global.apiPayload.exception.GeneralException; +import com.codiary.backend.global.domain.entity.Follow; +import com.codiary.backend.global.domain.entity.Member; +import com.codiary.backend.global.repository.FollowRepository; +import com.codiary.backend.global.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class FollowServiceImpl implements FollowService { + private final FollowRepository followRepository; + private final MemberRepository memberRepository; + + @Override + public Follow follow(Long toId, Member fromMember) { + //Validation: fromMember 존재 여부 확인/ toMember 존재 여부 확인/ 자기 자신 팔로우 불가/ 이미 팔로우 중인지 확인 + if (fromMember == null) { + throw new GeneralException(ErrorStatus.MEMBER_NOT_FOUND); + } + + Member toMember = memberRepository.findByToIdWithFollowers(toId) + .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); + + if (toId.equals(fromMember.getMemberId())) { + throw new GeneralException(ErrorStatus.MEMBER_SELF_FOLLOW); + } + + Follow follow = followRepository.findByFromMemberAndToMember(fromMember, toMember) + .orElse(null); + + // Business Logic: 팔로잉 여부에 따라 팔로잉 추가/삭제 + if (follow == null) { + follow = Follow.builder() + .fromMember(fromMember) + .toMember(toMember) + .followStatus(true) + .build(); + fromMember.getFollowings().add(follow); + toMember.getFollowers().add(follow); + } else { + if (follow.getFollowStatus()) { + follow.update(false); + fromMember.getFollowings().remove(follow); + toMember.getFollowers().remove(follow); + } else { + follow.update(true); + fromMember.getFollowings().add(follow); + toMember.getFollowers().add(follow); + } + } + followRepository.save(follow); + + // Response: 팔로우 정보 반환 + return follow; + } + + @Override + public Boolean isFollowing(Long toId, Member fromMember) { + //Validation: fromMember 존재 여부 확인/ toMember 존재 여부 확인/ 자기 자신 팔로우 불가 + if(fromMember == null){ + throw new GeneralException(ErrorStatus.MEMBER_NOT_FOUND); + } + if (toId.equals(fromMember.getMemberId())) { + throw new GeneralException(ErrorStatus.MEMBER_SELF_FOLLOW); + } + Member toMember = memberRepository.findById(toId) + .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); + + // Response: 팔로잉 여부 반환 + return followRepository.findByFromMemberAndToMember(fromMember, toMember) + .map(Follow::getFollowStatus) + .orElse(false); + } + + + @Override + public List getFollowings(Member member) { + //Validation: member 존재 여부 확인 + member = memberRepository.findByToIdWithFollowings(member.getMemberId()) + .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); + + //Business Logic: 팔로잉 리스트 조회 + List followings = followRepository.findByFromMemberAndFollowStatusTrueOrderByUpdatedAt(member); + + //Response: 팔로잉 리스트 반환 + return followings.stream() + .map(Follow::getToMember) + .collect(Collectors.toList()); + } + + @Override + public List getFollowers(Member member) { + //Validation: member 존재 여부 확인 + member = memberRepository.findByToIdWithFollowers(member.getMemberId()) + .orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND)); + + //Business Logic: 팔로워 리스트 조회 + List followers = followRepository.findByToMemberAndFollowStatusTrueOrderByUpdatedAt(member); + + //Response: 팔로워 리스트 반환 + return followers.stream() + .map(Follow::getFromMember) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/codiary/backend/global/web/controller/MemberController.java b/src/main/java/com/codiary/backend/global/web/controller/MemberController.java index f7e9aba5..45576731 100644 --- a/src/main/java/com/codiary/backend/global/web/controller/MemberController.java +++ b/src/main/java/com/codiary/backend/global/web/controller/MemberController.java @@ -1,8 +1,8 @@ package com.codiary.backend.global.web.controller; import com.codiary.backend.global.apiPayload.ApiResponse; +import com.codiary.backend.global.apiPayload.code.ErrorReasonDTO; import com.codiary.backend.global.converter.MemberConverter; -import com.codiary.backend.global.converter.PostConverter; import com.codiary.backend.global.domain.entity.*; import com.codiary.backend.global.domain.entity.mapping.MemberCategory; import com.codiary.backend.global.domain.enums.TechStack; @@ -10,24 +10,22 @@ import com.codiary.backend.global.service.MemberService.MemberQueryService; import com.codiary.backend.global.web.dto.Member.MemberRequestDTO; import com.codiary.backend.global.web.dto.Member.MemberResponseDTO; -import com.codiary.backend.global.web.dto.Post.PostResponseDTO; 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.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.codiary.backend.global.apiPayload.code.status.SuccessStatus; import com.codiary.backend.global.service.MemberService.FollowService; -import com.codiary.backend.global.web.dto.Member.FollowResponseDto; import com.codiary.backend.global.web.dto.Member.MemberSumResponseDto; import org.springframework.web.bind.annotation.*; @@ -87,12 +85,27 @@ public ApiResponse logout(@RequestHeader("Authorization") String token) description = "id를 가진 유저에 대해 팔로우하거나 취소할 수 있습니다." ) @PostMapping("/follow/{memberId}") - public ApiResponse follow(@PathVariable("memberId") Long toId) { - Member fromMember = memberCommandService.getRequester(); - Follow follow = followService.follow(toId, fromMember); + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MEMBER_1000", description = "성공입니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MEMBER_1001", description = "사용자가 없습니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class ))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "MEMBER_1100", description = "셀프 팔로우 기능은 제공하지 않습니다.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ErrorReasonDTO.class ))), + }) + public ApiResponse follow(@PathVariable("memberId") Long toId) { + Member member = memberCommandService.getRequester(); + + // 팔로우 요청 처리 + Follow follow = followService.follow(toId, member); + return ApiResponse.onSuccess(SuccessStatus.MEMBER_OK, MemberConverter.toManageFollowDto(follow)); } + @Operation( summary = "팔로우 여부 조회 기능", description = "id를 가진 유저에 대해 팔로우 했는지 여부를 확인할 수 있습니다. true-팔로우O / false-팔로우X" diff --git a/src/main/java/com/codiary/backend/global/web/dto/Member/FollowResponseDto.java b/src/main/java/com/codiary/backend/global/web/dto/Member/FollowResponseDto.java index 97470caa..62772a8f 100644 --- a/src/main/java/com/codiary/backend/global/web/dto/Member/FollowResponseDto.java +++ b/src/main/java/com/codiary/backend/global/web/dto/Member/FollowResponseDto.java @@ -1,8 +1,10 @@ package com.codiary.backend.global.web.dto.Member; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Builder; @Builder +@JsonInclude(JsonInclude.Include.NON_NULL) public record FollowResponseDto( Long followId, Long followerId, diff --git a/src/main/java/com/codiary/backend/global/web/dto/Member/MemberResponseDTO.java b/src/main/java/com/codiary/backend/global/web/dto/Member/MemberResponseDTO.java index ff01dda2..b38ec9c9 100644 --- a/src/main/java/com/codiary/backend/global/web/dto/Member/MemberResponseDTO.java +++ b/src/main/java/com/codiary/backend/global/web/dto/Member/MemberResponseDTO.java @@ -2,6 +2,7 @@ import com.codiary.backend.global.domain.enums.TechStack; import com.codiary.backend.global.jwt.TokenInfo; +import com.codiary.backend.global.web.dto.Team.TeamResponseDTO; import lombok.*; import java.time.LocalDateTime; @@ -97,7 +98,7 @@ public static class UserProfileDTO { String discordUrl; String introduction; List techStacksList; - List teamList; + List teamList; Boolean myPage; }