Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/run/backend/domain/crew/entity/JoinCrew.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class JoinCrew extends BaseEntity {
@JoinColumn(name = "crew_id")
private Crew crew;

void approveJoin() {
public void approveJoin() {
this.role = Role.MEMBER;
this.joinedDate = LocalDate.now();
this.joinStatus = JoinStatus.APPROVED;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package run.backend.domain.member.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import run.backend.domain.member.dto.response.MemberInfoResponse;
import run.backend.domain.member.entity.Member;
import run.backend.domain.member.service.MemberServiceImpl;
import run.backend.global.annotation.member.Login;
import run.backend.global.common.response.CommonResponse;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/members")
@Tag(name = "Members", description = "Member 관련 API")
public class MemberController {

private final MemberServiceImpl memberService;

@Operation(summary = "유저 정보 조회", description = "마이페이지 상단 유저 정보를 조회하는 API 입니다.")
@GetMapping
public CommonResponse<MemberInfoResponse> getMemberInfo(@Login Member member) {

MemberInfoResponse response = memberService.getMemberInfo(member);
return new CommonResponse<>("유저 정보 조회 성공", response);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
package run.backend.domain.member.dto.request;

public record MemberInfoRequest() {
import run.backend.domain.member.enums.Gender;

public record MemberInfoRequest(
Gender gender,
int age,
String nickname
) {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
package run.backend.domain.member.dto.response;

public record MemberInfoResponse() {
public record MemberInfoResponse(
String profileImageUrl,
String nickName,
String crewName
) {
}
6 changes: 6 additions & 0 deletions src/main/java/run/backend/domain/member/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ public class Member extends BaseEntity {

private boolean pushEnabled;

public void setMemberDefaultInfo(Gender gender, int age, String nickname) {
this.gender = gender;
this.age = age;
this.nickname = nickname;
}

@Builder
public Member(String username, String nickname, Gender gender, int age, String oauthId, OAuthType oauthType, String profileImage) {
this.username = username;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package run.backend.domain.member.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import run.backend.global.exception.ErrorCode;

@Getter
@AllArgsConstructor
public enum MemberErrorCode implements ErrorCode {

MEMBER_NOT_JOINED_CREW(5001, "가입한 크루가 없습니다.");

private final int errorCode;
private final String errorMessage;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package run.backend.domain.member.exception;

import run.backend.global.exception.CustomException;

public class MemberException extends CustomException {

public MemberException(final MemberErrorCode memberErrorCode) {
super(memberErrorCode);
}

public static class MemberNotJoinedCrew extends MemberException {
public MemberNotJoinedCrew() {
super(MemberErrorCode.MEMBER_NOT_JOINED_CREW);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import run.backend.domain.crew.entity.Crew;
import run.backend.domain.crew.enums.JoinStatus;
import run.backend.domain.member.entity.Member;

public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByOauthId(String oauthId);

@Query("""
SELECT jc.crew
FROM JoinCrew jc
WHERE jc.member.id = :memberId
AND jc.joinStatus = :status
""")
Comment on lines +14 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 쿼리dsl이구나~ 참고하겠습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

쿼리dsl은 아직.. ㅠㅜ 설정 추가하면 쿼리 dsl로 바꿀라구요!

Optional<Crew> findCrewByMemberIdAndStatus(@Param("memberId") Long memberId, @Param("status") JoinStatus status);
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package run.backend.domain.member.service;

import run.backend.domain.member.dto.request.MemberInfoRequest;
import run.backend.domain.member.dto.response.MemberInfoResponse;
import run.backend.domain.member.entity.Member;

public interface MemberService {

void updateMember(Long memberId, MemberInfoRequest memberInfoRequest);
MemberInfoResponse getMemberInfo(Member member);

MemberInfoResponse getMemberInfo(Long memberId);
// void updateMember();

void deleteMember(Long memberId);

void leaveCrew(Long memberId, Long crewId);

void joinCrew(String crewCode);
// void deleteMember(Member member);
//
// void leaveCrew(Member member, Long crewId);
//
// void joinCrew(String crewCode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package run.backend.domain.member.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import run.backend.domain.crew.entity.Crew;
import run.backend.domain.crew.enums.JoinStatus;
import run.backend.domain.member.dto.response.MemberInfoResponse;
import run.backend.domain.member.entity.Member;
import run.backend.domain.member.repository.MemberRepository;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberServiceImpl implements MemberService {

private final MemberRepository memberRepository;

@Override
public MemberInfoResponse getMemberInfo(Member member) {

String crewName = memberRepository.findCrewByMemberIdAndStatus(member.getId(), JoinStatus.APPROVED)
.map(Crew::getName)
.orElse("N/A");

return new MemberInfoResponse(member.getProfileImage(), member.getNickname(), crewName);
}
}
11 changes: 11 additions & 0 deletions src/main/java/run/backend/global/annotation/member/Login.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package run.backend.global.annotation.member;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Login {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package run.backend.global.annotation.member;

import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import run.backend.domain.member.entity.Member;
import run.backend.global.security.CustomUserDetails;

@Component
@RequiredArgsConstructor
public class LoginArgumentResolver implements HandlerMethodArgumentResolver {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

참고해서 크루도 이런식으로 만들어둘까요??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 네네 좋아요!!


@Override
public boolean supportsParameter(MethodParameter parameter) {

boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class);
boolean isMemberType = parameter.getParameterType().equals(Member.class);
return hasLoginAnnotation && isMemberType;
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
CustomUserDetails principal = (CustomUserDetails) authentication.getPrincipal();
return principal.getMember();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.web.bind.annotation.RestControllerAdvice;
import run.backend.domain.auth.exception.AuthException;
import run.backend.domain.file.exception.FileException;
import run.backend.domain.member.exception.MemberException;
import run.backend.global.common.response.CommonResponse;
import run.backend.global.exception.httpError.HttpErrorCode;

Expand All @@ -18,7 +19,8 @@ public class GlobalExceptionHandler {

@ExceptionHandler({
AuthException.RefreshTokenNotFound.class,
FileException.FileNotFound.class
FileException.FileNotFound.class,
MemberException.MemberNotJoinedCrew.class
})
public ResponseEntity<CommonResponse<Void>> handleNotFound(final CustomException e) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package run.backend.domain.member.service;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import run.backend.domain.crew.entity.Crew;
import run.backend.domain.crew.enums.JoinStatus;
import run.backend.domain.member.dto.response.MemberInfoResponse;
import run.backend.domain.member.entity.Member;
import run.backend.domain.member.enums.OAuthType;
import run.backend.domain.member.repository.MemberRepository;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class MemberServiceTest {

@Mock
private MemberRepository memberRepository;

@InjectMocks
private MemberServiceImpl memberService;

private Member testMember;
private Crew testCrew;

@BeforeEach
public void setUp() {

// member
testMember = Member.builder()
.username("test username")
.oauthId("test id")
.oauthType(OAuthType.GOOGLE)
.profileImage("test image")
.build();

// crew
testCrew = Crew.builder()
.name("test crew name")
.description("크루 소개 테스트")
.image("test image url")
.build();
}

@Test
@DisplayName("회원 정보가 올바르게 조회 되는지 확인")
public void getMemberInfoTest() {

// given
when(memberRepository.findCrewByMemberIdAndStatus(testMember.getId(), JoinStatus.APPROVED))
.thenReturn(Optional.of(testCrew));

// when
MemberInfoResponse response = memberService.getMemberInfo(testMember);

// then
assertEquals(testMember.getNickname(), response.nickName());
assertEquals(testCrew.getName(), response.crewName());
}
}