Skip to content

Commit 6047a37

Browse files
authored
Merge pull request #7 from NET-ZERO-FitFit/develop
[FEAT] main <- develop (member API 추가)
2 parents db0fc14 + 8eb8d6f commit 6047a37

15 files changed

Lines changed: 547 additions & 137 deletions

src/main/java/fitfit/domain/member/controller/MemberRestController.java

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
import fitfit.domain.member.dto.MemberResponseDTO;
77
import fitfit.domain.member.entity.Member;
88
import fitfit.domain.member.service.MemberCommandService;
9-
import fitfit.domain.token.service.MemberTokenCommandUseCase;
9+
import fitfit.domain.member.service.MemberQueryService;
10+
import fitfit.domain.token.service.MemberTokenCommandService;
1011
import fitfit.global.apiPayload.ApiResponse;
12+
import fitfit.global.apiPayload.code.status.SuccessStatus;
1113
import fitfit.global.enums.Provider;
1214
import io.swagger.v3.oas.annotations.Operation;
13-
import io.swagger.v3.oas.annotations.tags.Tag;
1415
import io.swagger.v3.oas.annotations.media.Content;
1516
import io.swagger.v3.oas.annotations.media.Schema;
1617
import io.swagger.v3.oas.annotations.responses.ApiResponses;
18+
import io.swagger.v3.oas.annotations.tags.Tag;
1719
import jakarta.validation.Valid;
1820
import lombok.RequiredArgsConstructor;
1921
import lombok.extern.slf4j.Slf4j;
@@ -27,8 +29,9 @@
2729
public class MemberRestController {
2830

2931
private final MemberCommandService memberCommandService;
30-
private final MemberTokenCommandUseCase memberTokenService;
32+
private final MemberTokenCommandService memberTokenService;
3133
private final KakaoOidcService kakaoOidcService;
34+
private final MemberQueryService memberQueryService;
3235

3336
@PostMapping("/auth/kko")
3437
@Operation(summary = "KAKAO OAuth2 로그인 API", description = "KAKAO OAuth2 로그인 API 입니다.")
@@ -61,7 +64,7 @@ public ApiResponse<MemberResponseDTO.KkoOAuth2LoginResponse> kkoOAuth2Login (@Va
6164
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "필수 약관에 동의하지 않았습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
6265
})
6366
public ApiResponse<MemberResponseDTO.TermAgreementResponse> termAgreement(
64-
@RequestHeader(value = "Authorization", required = false) String authorization,
67+
@RequestHeader(value = "Authorization", required = true) String authorization,
6568
@Valid @RequestBody MemberRequestDTO.TermAgreementRequest request) {
6669
return ApiResponse.onSuccess(memberCommandService.termAgreement(authorization, request));
6770
}
@@ -77,11 +80,119 @@ public ApiResponse<MemberResponseDTO.TermAgreementResponse> termAgreement(
7780
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "회원 필수 정보가 누락되었습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
7881
})
7982
public ApiResponse<MemberResponseDTO.MemberSignupResponse> signup(
80-
@RequestHeader(value = "Authorization", required = false) String authorization,
83+
@RequestHeader(value = "Authorization", required = true) String authorization,
8184
@Valid @RequestBody MemberRequestDTO.MemberSignupRequest request) {
8285
return ApiResponse.onSuccess(memberCommandService.memberSignup(authorization, request));
8386
}
8487

88+
@GetMapping("/mypage")
89+
@Operation(summary = "마이페이지 첫 화면 프로필 조회", description = "헤더의 토큰을 사용해 닉네임, 프사, 이메일, 클린지수 등을 조회하는 API입니다.")
90+
@ApiResponses({
91+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"),
92+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
93+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
94+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
95+
})
96+
public ApiResponse<MemberResponseDTO.MyProfileResDTO> getMyPage(
97+
@RequestHeader(value="Authorization", required = true) String authorization
98+
){
99+
MemberResponseDTO.MyProfileResDTO result = memberQueryService.getMyProfile(authorization);
100+
return ApiResponse.of(SuccessStatus._OK, result);
101+
102+
}
103+
104+
@GetMapping("/mypage/detail")
105+
@Operation(summary="프로필 상세 정보 조회(프로필 관리 페이지)", description = "프로필 관리 화면에 진입할 때, 기본 정보를 조회하는 API(체형 정보 제외)" )
106+
@ApiResponses({
107+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"),
108+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
109+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
110+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
111+
})
112+
public ApiResponse<MemberResponseDTO.MyProfileDetailDto> getMyProfileDetail(
113+
@RequestHeader(value="Authorization", required = true) String authorization
114+
115+
){
116+
return ApiResponse.of(SuccessStatus._OK, memberQueryService.getMyProfileDetail(authorization));
117+
}
118+
119+
@PatchMapping("/mypage/detail")
120+
@Operation(summary = "프로필 정보 수정", description = "프로필 사진 URL과 텍스트 정보를 수정하는 API")
121+
@ApiResponses({
122+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"),
123+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Bad Request, 잘못된 요청 형식", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
124+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
125+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
126+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
127+
})
128+
public ApiResponse<MemberResponseDTO.MyProfileDetailDto> updateMyProfile(
129+
@RequestHeader(value="Authorization", required = true) String authorization,
130+
@RequestBody @Valid MemberRequestDTO.UpdateProfileDTO req
131+
) {
132+
133+
return ApiResponse.of(SuccessStatus._OK, memberCommandService.updateProfile(authorization,req));
134+
}
135+
136+
@GetMapping("/mypage/body-info")
137+
@Operation(summary = "체형 정보 조회", description = "프로필 관리하기 -> 체형정보 수정하기에서 키, 몸무게, 전신사진URL을 조회하는 API")
138+
@ApiResponses({
139+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"),
140+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
141+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
142+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
143+
})
144+
public ApiResponse<MemberResponseDTO.BodyInfoResDTO> getBodyInfo(
145+
@RequestHeader(value="Authorization", required = true) String authorization
146+
){
147+
return ApiResponse.of(SuccessStatus._OK,memberQueryService.getBodyInfo(authorization));
148+
}
149+
150+
@PatchMapping("/mypage/body-info")
151+
@Operation(summary = "체형 정보 수정", description = "키, 몸무게, 전신사진URL을 수정하는 API")
152+
@ApiResponses({
153+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"),
154+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Bad Request, 잘못된 요청 형식", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
155+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
156+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
157+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
158+
})
159+
public ApiResponse<MemberResponseDTO.BodyInfoResDTO> updateBodyInfo(
160+
@RequestHeader(value="Authorization", required = true) String authorization,
161+
@RequestBody @Valid MemberRequestDTO.UpdateBodyInfoReqDTO req
162+
){
163+
return ApiResponse.of(SuccessStatus._OK,memberCommandService.updateBodyInfo(authorization,req));
164+
}
165+
166+
@GetMapping("/leaf")
167+
@Operation(summary = "나의 나뭇잎 조회", description = "현재 보유한 나뭇잎(포인트)를 조회하는 API")
168+
@ApiResponses({
169+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"),
170+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
171+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
172+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
173+
})
174+
public ApiResponse<MemberResponseDTO.PointResDTO> getMyPoing(
175+
@RequestHeader(value="Authorization", required = true) String authorization
176+
){
177+
return ApiResponse.of(SuccessStatus._OK,memberQueryService.getMyPoint(authorization));
178+
}
179+
180+
@PostMapping("/leaf/quiz")
181+
@Operation(summary = "퀴즈 결과 나뭇잎 적립", description = "맞춘 문제 개수를 받아 나뭇잎을 적립하는 API (1문제당 50잎)")
182+
@ApiResponses({
183+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "OK, 성공"),
184+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "Bad Request, 잘못된 요청 형식", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
185+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "유효하지 않은 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
186+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "만료된 JWT 토큰입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
187+
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
188+
})
189+
public ApiResponse<MemberResponseDTO.QuizPointResDTO> earnQuizPoint(
190+
@RequestHeader(value="Authorization", required = true) String authorization,
191+
@RequestBody @Valid MemberRequestDTO.QuizPointReqDTO req
192+
){
193+
return ApiResponse.of(SuccessStatus._OK,memberCommandService.earnQuizPoint(authorization,req));
194+
}
195+
85196
@PostMapping("/nickname/check")
86197
@Operation(summary = "닉네임 중복 확인 API", description = "닉네임 중복을 확인하는 API입니다.")
87198
@ApiResponses({
@@ -101,7 +212,7 @@ public ApiResponse<String> checkNickname(@Valid @RequestBody MemberRequestDTO.Ni
101212
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "401", description = "인증이 필요합니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))),
102213
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "존재하지 않는 회원입니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class)))
103214
})
104-
public ApiResponse<String> withdrawMember(@RequestHeader("Authorization") String authorization) {
215+
public ApiResponse<String> withdrawMember(@RequestHeader(value = "Authorization", required = true) String authorization) {
105216
memberCommandService.withdrawMember(authorization);
106217
return ApiResponse.onSuccess("회원 탈퇴가 성공적으로 처리되었습니다.");
107218
}

src/main/java/fitfit/domain/member/converter/MemberConverter.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import fitfit.global.enums.MemberStatus;
1010
import fitfit.global.enums.Provider;
1111

12+
import java.util.List;
13+
1214
public class MemberConverter {
1315
private static final String DEFAULT_PROFILE_IMG_URL = "https://fitfit-profile-img.s3.ap-northeast-2.amazonaws.com/default_img.png";
1416
public static Member toMember (MemberDataDTO.MemberData kakaoMemberData, Provider provider) {
@@ -25,7 +27,7 @@ public static Member toMember (MemberDataDTO.MemberData kakaoMemberData, Provide
2527
.height("임시 키")
2628
.weight("임시 체중")
2729
.point(0)
28-
.clean_index(50)
30+
.cleanIndex(50)
2931
.build();
3032
}
3133

@@ -51,4 +53,51 @@ public static MemberResponseDTO.MemberSignupResponse toMemberSignupResponse(Memb
5153
.status(MemberStatus.ACTIVE)
5254
.build();
5355
}
56+
57+
//Entity -> DTO
58+
public static MemberResponseDTO.MyProfileResDTO toMemberProfileResDTO(Member member) {
59+
return MemberResponseDTO.MyProfileResDTO.builder()
60+
.memberId(member.getId())
61+
.nickname(member.getNickname())
62+
.email(member.getEmail())
63+
.profileImgUrl(member.getProfileImgUrl())
64+
.cleanIndex(member.getCleanIndex())
65+
.build();
66+
}
67+
68+
//Entity + StyleList -> Detail DTO
69+
public static MemberResponseDTO.MyProfileDetailDto toMemberProfileDetailResDTO(Member member, List<String> styleList){
70+
return MemberResponseDTO.MyProfileDetailDto.builder()
71+
.memberId(member.getId())
72+
.name(member.getName())
73+
.nickname(member.getNickname())
74+
.profileImgUrl(member.getProfileImgUrl())
75+
.birthDate(member.getBirth())
76+
.phoneNumber(member.getPhoneNumber())
77+
.preferredStyles(styleList)
78+
.build();
79+
}
80+
81+
//Member Entity -> BodyInfoResDTO
82+
public static MemberResponseDTO.BodyInfoResDTO toMemberBodyInfoResDTO(Member member) {
83+
return MemberResponseDTO.BodyInfoResDTO.builder()
84+
.height(member.getHeight())
85+
.weight(member.getWeight())
86+
.bodyImgUrl(member.getFullBodyImgUrl())
87+
.build();
88+
}
89+
90+
//Member Entity -> PointResDTO
91+
public static MemberResponseDTO.PointResDTO toPointResDTO(Member member){
92+
return MemberResponseDTO.PointResDTO.builder()
93+
.point(member.getPoint()!= null ? member.getPoint():0)
94+
.build();
95+
}
96+
97+
public static MemberResponseDTO.QuizPointResDTO toQuizPointResDTO(Member member, Integer earnedPoint) {
98+
return MemberResponseDTO.QuizPointResDTO.builder()
99+
.earnedPoint(earnedPoint)
100+
.totalPoint(member.getPoint())
101+
.build();
102+
}
54103
}

src/main/java/fitfit/domain/member/dto/MemberRequestDTO.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import fitfit.global.enums.Gender;
44
import fitfit.global.enums.Style;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import jakarta.validation.constraints.Min;
7+
import jakarta.validation.constraints.NotBlank;
58
import jakarta.validation.constraints.NotNull;
69
import jakarta.validation.constraints.Size;
710
import lombok.AllArgsConstructor;
@@ -77,4 +80,38 @@ public static class MemberSignupRequest {
7780
private List<Style> styleList;
7881
private String fullBodyImgUrl;
7982
}
83+
84+
public record UpdateProfileDTO(
85+
86+
@Schema(description = "닉네임", example = "박콩")
87+
@NotBlank(message="닉네임은 필수입니다.")
88+
String nickname,
89+
90+
@Schema(description = "프로필 이미지 URL", example = "https://s3...")
91+
String profileImgUrl,
92+
93+
@Schema(description = "선호 스타일 리스트", example = "[\"BASIC\", \"CASUAL\"]")
94+
List<String> preferredStyles
95+
){}
96+
97+
public record UpdateBodyInfoReqDTO(
98+
99+
@Schema(description = "키", example="170")
100+
@NotBlank(message="키 입력은 필수입니다.")
101+
String height,
102+
103+
@Schema(description = "몸무게", example="45")
104+
@NotBlank(message="몸무게 입력은 필수입니다.")
105+
String weight,
106+
107+
@Schema(description = "전신사진 URL", example = "https://s3...")
108+
String bodyImgUrl
109+
){}
110+
111+
public record QuizPointReqDTO(
112+
@Schema(description = "획득한 포인트", example = "100")
113+
@NotNull(message="획득한 포인트는 필수입니다.")
114+
@Min(value = 0, message = "포인트는 0 이상이어야 합니다.")
115+
Integer earnedPoint
116+
){}
80117
}

0 commit comments

Comments
 (0)