diff --git a/build.gradle b/build.gradle index f7db427..b2170a0 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,6 @@ dependencies { testImplementation 'com.squareup.okhttp3:mockwebserver:4.12.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine' - runtimeOnly 'com.mysql:mysql-connector-j' } tasks.named('test') { diff --git a/src/main/java/com/project/growfit/domain/User/controller/UserController.java b/src/main/java/com/project/growfit/domain/User/controller/UserController.java new file mode 100644 index 0000000..95fb1ef --- /dev/null +++ b/src/main/java/com/project/growfit/domain/User/controller/UserController.java @@ -0,0 +1,60 @@ +package com.project.growfit.domain.User.controller; + +import com.project.growfit.domain.User.dto.request.ChildInfoRequestDto; +import com.project.growfit.domain.User.dto.request.ParentInfoRequestDto; +import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; +import com.project.growfit.domain.User.dto.response.ParentInfoResponseDto; +import com.project.growfit.domain.User.service.UserService; +import com.project.growfit.global.response.ResultCode; +import com.project.growfit.global.response.ResultResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") +@Tag(name = "유저 정보 API") +public class UserController { + + private final UserService userService; + + @Operation(summary = "부모 정보 조회") + @GetMapping("/parent") + public ResultResponse getParentInfo() { + ParentInfoResponseDto dto = userService.getParentInfo(); + return ResultResponse.of(ResultCode.INFO_SUCCESS, dto); + } + + @Operation(summary = "아이 정보 조회") + @GetMapping("/child") + public ResultResponse getChildInfo() { + ChildInfoResponseDto dto = userService.getChildInfo(); + return ResultResponse.of(ResultCode.INFO_SUCCESS, dto); + } + + @Operation(summary = "부모 정보 수정 (이메일 수정 시 JWT 재발급)") + @PutMapping(value = "/parent", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResultResponse updateParentInfo(@Valid @RequestPart("info") ParentInfoRequestDto request, + @RequestPart(value = "image", required = false) MultipartFile image, + HttpServletResponse response + ) throws IOException { + userService.updateParentInfo(request, image, response); + return ResultResponse.of(ResultCode.UPDATE_SUCCESS, "부모 정보가 수정되었습니다."); + } + + @Operation(summary = "아이 정보 수정") + @PutMapping("/child") + public ResultResponse updateChildInfo(@Valid @RequestBody ChildInfoRequestDto request) { + userService.updateChildInfo(request); + return ResultResponse.of(ResultCode.UPDATE_SUCCESS, "아이 정보가 수정되었습니다."); + } +} diff --git a/src/main/java/com/project/growfit/domain/User/dto/request/ChildInfoRequestDto.java b/src/main/java/com/project/growfit/domain/User/dto/request/ChildInfoRequestDto.java new file mode 100644 index 0000000..1d1d8a1 --- /dev/null +++ b/src/main/java/com/project/growfit/domain/User/dto/request/ChildInfoRequestDto.java @@ -0,0 +1,37 @@ +package com.project.growfit.domain.User.dto.request; + +import com.project.growfit.domain.User.entity.ChildGender; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.*; + +public record ChildInfoRequestDto( + @NotBlank(message = "닉네임은 필수입니다.") + @Schema(description = "자녀 닉네임", example = "민준이") + String nickname, + + @Pattern( + regexp = "^(?=.*[a-z])(?=.*\\d)[a-z\\d]{8,}$", + message = "비밀번호는 소문자와 숫자를 포함해 8자 이상이어야 합니다." + ) + @Schema(description = "자녀 비밀번호", example = "secure123") + String password, + + @NotNull(message = "성별을 입력해주세요.") + @Schema(description = "자녀 성별", example = "MALE") + ChildGender gender, + + @Min(value = 1, message = "나이는 1세 이상이어야 합니다.") + @Schema(description = "자녀 나이", example = "11") + int age, + + @Schema(description = "아이 키 (cm)", example = "110") + @Min(value = 30, message = "키는 30cm 이상이어야 합니다.") + @Max(value = 250, message = "키는 200cm 이하이어야 합니다.") + long height, + + @Schema(description = "아이 몸무게 (kg)", example = "20") + @Min(value = 5, message = "몸무게는 5kg 이상이어야 합니다.") + @Max(value = 150, message = "몸무게는 150kg 이하이어야 합니다.") + long weight +) { +} diff --git a/src/main/java/com/project/growfit/domain/User/dto/request/ParentInfoRequestDto.java b/src/main/java/com/project/growfit/domain/User/dto/request/ParentInfoRequestDto.java new file mode 100644 index 0000000..4411549 --- /dev/null +++ b/src/main/java/com/project/growfit/domain/User/dto/request/ParentInfoRequestDto.java @@ -0,0 +1,40 @@ +package com.project.growfit.domain.User.dto.request; + +import com.project.growfit.domain.User.entity.ChildGender; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.*; + +public record ParentInfoRequestDto( + + @NotBlank(message = "부모 닉네임은 필수입니다.") + @Schema(description = "부모 닉네임", example = "민준맘") + String nickname, + + @Email(message = "유효한 이메일 형식이어야 합니다.") + @NotBlank(message = "이메일은 필수입니다.") + @Schema(description = "부모 이메일", example = "parent@example.com") + String email, + + @NotBlank(message = "자녀 이름은 필수입니다.") + @Schema(description = "자녀 이름", example = "김민준") + String childName, + + @Min(value = 1, message = "자녀 나이는 1세 이상이어야 합니다.") + @Schema(description = "자녀 나이", example = "11") + int childAge, + + @NotNull(message = "자녀 성별은 필수입니다.") + @Schema(description = "자녀 성별", example = "MALE") + ChildGender childGender, + + @Schema(description = "아이 키 (cm)", example = "110") + @Min(value = 30, message = "키는 30cm 이상이어야 합니다.") + @Max(value = 250, message = "키는 200cm 이하이어야 합니다.") + long childHeight, + + @Schema(description = "아이 몸무게 (kg)", example = "20") + @Min(value = 5, message = "몸무게는 5kg 이상이어야 합니다.") + @Max(value = 150, message = "몸무게는 150kg 이하이어야 합니다.") + long childWeight +) { +} diff --git a/src/main/java/com/project/growfit/domain/User/dto/response/ChildInfoResponseDto.java b/src/main/java/com/project/growfit/domain/User/dto/response/ChildInfoResponseDto.java index f6379b6..f923fe8 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/response/ChildInfoResponseDto.java +++ b/src/main/java/com/project/growfit/domain/User/dto/response/ChildInfoResponseDto.java @@ -10,7 +10,6 @@ public record ChildInfoResponseDto( ChildGender child_gender, int child_age, ChildBodyInfoResponseDto child_BodyInfo - ) { public static ChildInfoResponseDto toDto(Child child) { return new ChildInfoResponseDto( diff --git a/src/main/java/com/project/growfit/domain/User/dto/response/ParentInfoResponseDto.java b/src/main/java/com/project/growfit/domain/User/dto/response/ParentInfoResponseDto.java new file mode 100644 index 0000000..8ba10ee --- /dev/null +++ b/src/main/java/com/project/growfit/domain/User/dto/response/ParentInfoResponseDto.java @@ -0,0 +1,17 @@ +package com.project.growfit.domain.User.dto.response; + +import com.project.growfit.domain.User.entity.Parent; + +public record ParentInfoResponseDto( + String nickname, + String profileImage, + ChildInfoResponseDto child +) { + public static ParentInfoResponseDto toDto(Parent parent) { + return new ParentInfoResponseDto( + parent.getNickname(), + parent.getProviderId(), + ChildInfoResponseDto.toDto(parent.getChildren().get(0)) + ); + } +} diff --git a/src/main/java/com/project/growfit/domain/User/entity/Child.java b/src/main/java/com/project/growfit/domain/User/entity/Child.java index d91a8c7..17f83f6 100644 --- a/src/main/java/com/project/growfit/domain/User/entity/Child.java +++ b/src/main/java/com/project/growfit/domain/User/entity/Child.java @@ -108,7 +108,6 @@ public String user_password() { public void addRegister(Parent parent) {this.parent = parent;} public void updateCode(String code) {this.codeNumber = code;} - public void updateNickname(String nickname){this.nickname = nickname;} public void updatePassword(String password){ this.password = password;} @@ -119,7 +118,15 @@ public void updateCredentials(String id, String password, String nickname) { } public ChildBodyInfo getLatestBodyInfo() { - return bodyInfoList.isEmpty() ? null : bodyInfoList.get(0); + return bodyInfoList.isEmpty() ? null : bodyInfoList.getLast(); + } + + public void updateInfo(int age, ChildGender gender, String name, String nickname, long height, long weight) { + this.age = age; + this.gender = gender; + if (name != null) this.name = name; + if (nickname != null) this.nickname = nickname; + this.bodyInfoList.add(new ChildBodyInfo(height, weight, this)); } } diff --git a/src/main/java/com/project/growfit/domain/User/entity/Parent.java b/src/main/java/com/project/growfit/domain/User/entity/Parent.java index d86acf5..6c9e7f3 100644 --- a/src/main/java/com/project/growfit/domain/User/entity/Parent.java +++ b/src/main/java/com/project/growfit/domain/User/entity/Parent.java @@ -89,10 +89,19 @@ public boolean hasChildWithName(String childName) { .anyMatch(child -> child.getName().equals(childName)); } - public void updateNickname(String nickname){ + public void updateInfo(String nickname){ this.nickname = nickname; } + public void updateInfo(String nickname, String profile){ + this.nickname = nickname; + this.photo = profile; + } + + public void updateEmail(String email){ + this.email = email; + } + public void addChild(Child child) { this.children.add(child); child.addRegister(this); diff --git a/src/main/java/com/project/growfit/domain/User/service/UserService.java b/src/main/java/com/project/growfit/domain/User/service/UserService.java new file mode 100644 index 0000000..fe2d7cf --- /dev/null +++ b/src/main/java/com/project/growfit/domain/User/service/UserService.java @@ -0,0 +1,17 @@ +package com.project.growfit.domain.User.service; + +import com.project.growfit.domain.User.dto.request.ChildInfoRequestDto; +import com.project.growfit.domain.User.dto.request.ParentInfoRequestDto; +import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; +import com.project.growfit.domain.User.dto.response.ParentInfoResponseDto; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +public interface UserService { + + ParentInfoResponseDto getParentInfo(); + ChildInfoResponseDto getChildInfo(); + + void updateParentInfo(ParentInfoRequestDto request, MultipartFile image, HttpServletResponse response); + void updateChildInfo(ChildInfoRequestDto request); +} diff --git a/src/main/java/com/project/growfit/domain/User/service/impl/UserServiceImpl.java b/src/main/java/com/project/growfit/domain/User/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..2b9fba6 --- /dev/null +++ b/src/main/java/com/project/growfit/domain/User/service/impl/UserServiceImpl.java @@ -0,0 +1,85 @@ +package com.project.growfit.domain.User.service.impl; + +import com.project.growfit.domain.User.dto.request.ChildInfoRequestDto; +import com.project.growfit.domain.User.dto.request.ParentInfoRequestDto; +import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; +import com.project.growfit.domain.User.dto.response.ParentInfoResponseDto; +import com.project.growfit.domain.User.entity.Child; +import com.project.growfit.domain.User.entity.Parent; +import com.project.growfit.domain.User.service.UserService; +import com.project.growfit.global.auth.jwt.JwtProvider; +import com.project.growfit.global.auth.service.AuthenticatedUserProvider; +import com.project.growfit.global.exception.BusinessException; +import com.project.growfit.global.exception.ErrorCode; +import com.project.growfit.global.s3.service.S3UploadService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +@Service +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { + + private final AuthenticatedUserProvider userProvider; + private final S3UploadService s3UploadService; + private final JwtProvider jwtProvider; + private final PasswordEncoder passwordEncoder; + + private String imageUploadPath = "user/"; + + @Override + @Transactional(readOnly = true) + public ParentInfoResponseDto getParentInfo() { + Parent parent = userProvider.getAuthenticatedParent(); + return ParentInfoResponseDto.toDto(parent); + } + + @Override + @Transactional(readOnly = true) + public ChildInfoResponseDto getChildInfo() { + Child child = userProvider.getAuthenticatedChild(); + return ChildInfoResponseDto.toDto(child); + } + + + @Override + @Transactional + public void updateParentInfo(ParentInfoRequestDto request, MultipartFile image, HttpServletResponse response) { + Parent parent = userProvider.getAuthenticatedParent(); + + if (image == null || image.isEmpty()) throw new BusinessException(ErrorCode.EMPTY_IMAGE_FILE); + String imageUrl; + try { + imageUrl = s3UploadService.saveFile(image, imageUploadPath); + } catch (Exception e) { + throw new BusinessException(ErrorCode.FILE_UPLOAD_ERROR); + } + + boolean isEmailChanged = !parent.getEmail().equals(request.email()); + String oldEmail = parent.getEmail(); + parent.updateInfo(request.nickname(), imageUrl); + if (isEmailChanged) parent.updateEmail(request.email()); + + Child child = parent.getChildren().get(0); + child.updateInfo(request.childAge(), request.childGender(), request.childName(), null, request.childHeight(), request.childWeight()); + + if (isEmailChanged) jwtProvider.regenerateToken(oldEmail, request.email(), String.valueOf(parent.getRole()), "SOCIAL_KAKAO", response); + } + + @Override + @Transactional + public void updateChildInfo(ChildInfoRequestDto request) { + Child child = userProvider.getAuthenticatedChild(); + if (request.password() != null && + !request.password().isBlank() && + !passwordEncoder.matches(request.password(), child.getPassword())) { + + String encodedPassword = passwordEncoder.encode(request.password()); + child.updatePassword(encodedPassword); + } + child.updateInfo(request.age(), request.gender(), null, request.nickname(), request.height(), request.weight()); + } +} diff --git a/src/main/java/com/project/growfit/domain/User/controller/AuthChildController.java b/src/main/java/com/project/growfit/domain/auth/controller/AuthChildController.java similarity index 91% rename from src/main/java/com/project/growfit/domain/User/controller/AuthChildController.java rename to src/main/java/com/project/growfit/domain/auth/controller/AuthChildController.java index ad70de4..f1f0a50 100644 --- a/src/main/java/com/project/growfit/domain/User/controller/AuthChildController.java +++ b/src/main/java/com/project/growfit/domain/auth/controller/AuthChildController.java @@ -1,10 +1,10 @@ -package com.project.growfit.domain.User.controller; +package com.project.growfit.domain.auth.controller; -import com.project.growfit.domain.User.dto.request.AuthChildRequestDto; -import com.project.growfit.domain.User.dto.request.FindChildPasswordRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthChildRequestDto; +import com.project.growfit.domain.auth.dto.request.FindChildPasswordRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildResponseDto; -import com.project.growfit.domain.User.service.AuthChildService; +import com.project.growfit.domain.auth.dto.response.ChildResponseDto; +import com.project.growfit.domain.auth.service.AuthChildService; import com.project.growfit.global.response.ResultCode; import com.project.growfit.global.response.ResultResponse; import io.swagger.v3.oas.annotations.Operation; diff --git a/src/main/java/com/project/growfit/domain/User/controller/AuthParentController.java b/src/main/java/com/project/growfit/domain/auth/controller/AuthParentController.java similarity index 80% rename from src/main/java/com/project/growfit/domain/User/controller/AuthParentController.java rename to src/main/java/com/project/growfit/domain/auth/controller/AuthParentController.java index 571e0e9..4a4c65a 100644 --- a/src/main/java/com/project/growfit/domain/User/controller/AuthParentController.java +++ b/src/main/java/com/project/growfit/domain/auth/controller/AuthParentController.java @@ -1,10 +1,10 @@ -package com.project.growfit.domain.User.controller; +package com.project.growfit.domain.auth.controller; import com.google.zxing.WriterException; -import com.project.growfit.domain.User.dto.request.AuthParentRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthParentRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildQrCodeResponseDto; -import com.project.growfit.domain.User.service.AuthParentService; +import com.project.growfit.domain.auth.dto.response.ChildQrCodeResponseDto; +import com.project.growfit.domain.auth.service.AuthParentService; import com.project.growfit.global.auth.dto.CustomUserDetails; import com.project.growfit.global.response.ResultCode; import com.project.growfit.global.response.ResultResponse; @@ -35,7 +35,7 @@ public ResultResponse registerChild(@Valid @RequestBody Au @Operation(summary = "아이 QR 코드 생성") @GetMapping("/child/qr") - public ResultResponse createQrCode(@AuthenticationPrincipal CustomUserDetails user) throws WriterException { + public ResultResponse createQrCode() throws WriterException { ChildQrCodeResponseDto dto = parentService.createQR(); return ResultResponse.of(ResultCode.QR_GENERATION_SUCCESS, dto); diff --git a/src/main/java/com/project/growfit/domain/User/controller/OAuthController.java b/src/main/java/com/project/growfit/domain/auth/controller/OAuthController.java similarity index 94% rename from src/main/java/com/project/growfit/domain/User/controller/OAuthController.java rename to src/main/java/com/project/growfit/domain/auth/controller/OAuthController.java index 22b089c..46108af 100644 --- a/src/main/java/com/project/growfit/domain/User/controller/OAuthController.java +++ b/src/main/java/com/project/growfit/domain/auth/controller/OAuthController.java @@ -1,7 +1,7 @@ -package com.project.growfit.domain.User.controller; +package com.project.growfit.domain.auth.controller; -import com.project.growfit.domain.User.dto.response.ParentLoginResponseDto; -import com.project.growfit.domain.User.service.OauthService; +import com.project.growfit.domain.auth.dto.response.ParentLoginResponseDto; +import com.project.growfit.domain.auth.service.OauthService; import com.project.growfit.global.exception.BusinessException; import com.project.growfit.global.response.ResultCode; import com.project.growfit.global.response.ResultResponse; diff --git a/src/main/java/com/project/growfit/domain/User/controller/PageController.java b/src/main/java/com/project/growfit/domain/auth/controller/PageController.java similarity index 86% rename from src/main/java/com/project/growfit/domain/User/controller/PageController.java rename to src/main/java/com/project/growfit/domain/auth/controller/PageController.java index fee89e0..9e93caa 100644 --- a/src/main/java/com/project/growfit/domain/User/controller/PageController.java +++ b/src/main/java/com/project/growfit/domain/auth/controller/PageController.java @@ -1,4 +1,4 @@ -package com.project.growfit.domain.User.controller; +package com.project.growfit.domain.auth.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; diff --git a/src/main/java/com/project/growfit/domain/User/controller/TestAuthController.java b/src/main/java/com/project/growfit/domain/auth/controller/TestAuthController.java similarity index 94% rename from src/main/java/com/project/growfit/domain/User/controller/TestAuthController.java rename to src/main/java/com/project/growfit/domain/auth/controller/TestAuthController.java index fbb5f3f..70cdd0a 100644 --- a/src/main/java/com/project/growfit/domain/User/controller/TestAuthController.java +++ b/src/main/java/com/project/growfit/domain/auth/controller/TestAuthController.java @@ -1,6 +1,6 @@ -package com.project.growfit.domain.User.controller; +package com.project.growfit.domain.auth.controller; -import com.project.growfit.domain.User.dto.response.ParentLoginResponseDto; +import com.project.growfit.domain.auth.dto.response.ParentLoginResponseDto; import com.project.growfit.global.auth.cookie.CookieService; import com.project.growfit.global.auth.jwt.JwtProvider; import com.project.growfit.global.redis.entity.TokenRedis; diff --git a/src/main/java/com/project/growfit/domain/User/dto/request/AuthChildRequestDto.java b/src/main/java/com/project/growfit/domain/auth/dto/request/AuthChildRequestDto.java similarity index 55% rename from src/main/java/com/project/growfit/domain/User/dto/request/AuthChildRequestDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/request/AuthChildRequestDto.java index 5f6f156..ceae551 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/request/AuthChildRequestDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/request/AuthChildRequestDto.java @@ -1,23 +1,26 @@ -package com.project.growfit.domain.User.dto.request; +package com.project.growfit.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.*; public record AuthChildRequestDto( @Schema(description = "아이 로그인 ID", example = "child123") @NotBlank(message = "아이디를 입력해주세요.") + @Size(min = 4, max = 20, message = "아이디는 4자 이상 20자 이하로 입력해주세요.") String childId, @Schema(description = "아이 로그인 비밀번호", example = "password123") @NotBlank(message = "비밀번호를 입력해주세요.") - @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d@$!%*?&]{8,}$", - message = "비밀번호는 8자 이상, 영문자와 숫자를 포함해야 합니다.") + @Pattern( + regexp = "^(?=.*[a-z])(?=.*\\d)[a-z\\d@$!%*?&]{8,}$", + message = "비밀번호는 소문자와 숫자를 포함하여 8자 이상이어야 합니다." + ) String childPassword, @Schema(description = "닉네임 입력", example = "민준콩") @NotBlank(message = "닉네임을 입력해주세요.") + @Size(min = 2, message = "닉네임은 2자 이상이어야 합니다.") String nickname ) { } \ No newline at end of file diff --git a/src/main/java/com/project/growfit/domain/User/dto/request/AuthParentRequestDto.java b/src/main/java/com/project/growfit/domain/auth/dto/request/AuthParentRequestDto.java similarity index 73% rename from src/main/java/com/project/growfit/domain/User/dto/request/AuthParentRequestDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/request/AuthParentRequestDto.java index e9b2ec3..8c2d6f5 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/request/AuthParentRequestDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/request/AuthParentRequestDto.java @@ -1,18 +1,18 @@ -package com.project.growfit.domain.User.dto.request; +package com.project.growfit.domain.auth.dto.request; import com.project.growfit.domain.User.entity.ChildGender; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.*; public record AuthParentRequestDto( @Schema(description = "등록할 부모 닉네임", example = "건강한 엄마") @NotBlank(message = "닉네임은 필수 입력값입니다.") + @Size(min = 2, message = "닉네임은 2자 이상이어야 합니다.") String nickname, @Schema(description = "아이 이름", example = "김민준") @NotBlank(message = "아이 이름을 입력해주세요.") + @Size(min = 2, message = "아이 이름은 2자 이상이어야 합니다.") String child_name, @Schema(description = "아이 성별", example = "MALE") @@ -25,10 +25,12 @@ public record AuthParentRequestDto( @Schema(description = "아이 키 (cm)", example = "110") @Min(value = 30, message = "키는 30cm 이상이어야 합니다.") + @Max(value = 250, message = "키는 200cm 이하이어야 합니다.") long child_height, @Schema(description = "아이 몸무게 (kg)", example = "20") @Min(value = 5, message = "몸무게는 5kg 이상이어야 합니다.") + @Max(value = 150, message = "몸무게는 150kg 이하이어야 합니다.") long child_weight ) { } diff --git a/src/main/java/com/project/growfit/domain/User/dto/request/FindChildPasswordRequestDto.java b/src/main/java/com/project/growfit/domain/auth/dto/request/FindChildPasswordRequestDto.java similarity index 94% rename from src/main/java/com/project/growfit/domain/User/dto/request/FindChildPasswordRequestDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/request/FindChildPasswordRequestDto.java index d7e47ff..c41a1f0 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/request/FindChildPasswordRequestDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/request/FindChildPasswordRequestDto.java @@ -1,4 +1,4 @@ -package com.project.growfit.domain.User.dto.request; +package com.project.growfit.domain.auth.dto.request; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/project/growfit/domain/User/dto/request/ParentOAuthRequestDto.java b/src/main/java/com/project/growfit/domain/auth/dto/request/ParentOAuthRequestDto.java similarity index 71% rename from src/main/java/com/project/growfit/domain/User/dto/request/ParentOAuthRequestDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/request/ParentOAuthRequestDto.java index 0b0cf9a..0ab4e78 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/request/ParentOAuthRequestDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/request/ParentOAuthRequestDto.java @@ -1,17 +1,19 @@ -package com.project.growfit.domain.User.dto.request; +package com.project.growfit.domain.auth.dto.request; import com.project.growfit.domain.User.entity.Parent; import com.project.growfit.domain.User.entity.ROLE; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.*; public record ParentOAuthRequestDto( @Schema(description = "이메일", example = "example@naver.com") @NotBlank(message = "이메일을 입력해주세요.") + @Email(message = "유효한 이메일 형식이어야 합니다.") String email, - @Schema(description = "닉네임", example = "해윤맘") + @Schema(description = "닉네임", example = "맘맘맘") @NotBlank(message = "닉네임을 입력해주세요.") + @Size(min = 2, message = "닉네임은 2자 이상이어야 합니다.") String nickname, @Schema(description = "소셜 고유 ID", example = "kakao_1234567890") diff --git a/src/main/java/com/project/growfit/domain/User/dto/response/ChildQrCodeResponseDto.java b/src/main/java/com/project/growfit/domain/auth/dto/response/ChildQrCodeResponseDto.java similarity index 87% rename from src/main/java/com/project/growfit/domain/User/dto/response/ChildQrCodeResponseDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/response/ChildQrCodeResponseDto.java index 0f7756d..7362a06 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/response/ChildQrCodeResponseDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/response/ChildQrCodeResponseDto.java @@ -1,4 +1,4 @@ -package com.project.growfit.domain.User.dto.response; +package com.project.growfit.domain.auth.dto.response; import com.project.growfit.domain.User.entity.Child; diff --git a/src/main/java/com/project/growfit/domain/User/dto/response/ChildResponseDto.java b/src/main/java/com/project/growfit/domain/auth/dto/response/ChildResponseDto.java similarity index 91% rename from src/main/java/com/project/growfit/domain/User/dto/response/ChildResponseDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/response/ChildResponseDto.java index 9e3b06e..f04fb4e 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/response/ChildResponseDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/response/ChildResponseDto.java @@ -1,4 +1,4 @@ -package com.project.growfit.domain.User.dto.response; +package com.project.growfit.domain.auth.dto.response; import com.project.growfit.domain.User.entity.Child; diff --git a/src/main/java/com/project/growfit/domain/User/dto/response/ParentLoginResponseDto.java b/src/main/java/com/project/growfit/domain/auth/dto/response/ParentLoginResponseDto.java similarity index 63% rename from src/main/java/com/project/growfit/domain/User/dto/response/ParentLoginResponseDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/response/ParentLoginResponseDto.java index c4ddeda..d6b33c6 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/response/ParentLoginResponseDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/response/ParentLoginResponseDto.java @@ -1,4 +1,4 @@ -package com.project.growfit.domain.User.dto.response; +package com.project.growfit.domain.auth.dto.response; public record ParentLoginResponseDto( String email, diff --git a/src/main/java/com/project/growfit/domain/User/dto/response/ParentResponseDto.java b/src/main/java/com/project/growfit/domain/auth/dto/response/ParentResponseDto.java similarity index 90% rename from src/main/java/com/project/growfit/domain/User/dto/response/ParentResponseDto.java rename to src/main/java/com/project/growfit/domain/auth/dto/response/ParentResponseDto.java index b32f02d..14ab061 100644 --- a/src/main/java/com/project/growfit/domain/User/dto/response/ParentResponseDto.java +++ b/src/main/java/com/project/growfit/domain/auth/dto/response/ParentResponseDto.java @@ -1,4 +1,4 @@ -package com.project.growfit.domain.User.dto.response; +package com.project.growfit.domain.auth.dto.response; import com.project.growfit.domain.User.entity.Parent; diff --git a/src/main/java/com/project/growfit/domain/User/service/AuthChildService.java b/src/main/java/com/project/growfit/domain/auth/service/AuthChildService.java similarity index 71% rename from src/main/java/com/project/growfit/domain/User/service/AuthChildService.java rename to src/main/java/com/project/growfit/domain/auth/service/AuthChildService.java index 19cafd1..56a66a8 100644 --- a/src/main/java/com/project/growfit/domain/User/service/AuthChildService.java +++ b/src/main/java/com/project/growfit/domain/auth/service/AuthChildService.java @@ -1,9 +1,9 @@ -package com.project.growfit.domain.User.service; +package com.project.growfit.domain.auth.service; -import com.project.growfit.domain.User.dto.request.AuthChildRequestDto; -import com.project.growfit.domain.User.dto.request.FindChildPasswordRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthChildRequestDto; +import com.project.growfit.domain.auth.dto.request.FindChildPasswordRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildResponseDto; +import com.project.growfit.domain.auth.dto.response.ChildResponseDto; import jakarta.servlet.http.HttpServletResponse; public interface AuthChildService { diff --git a/src/main/java/com/project/growfit/domain/User/service/AuthParentService.java b/src/main/java/com/project/growfit/domain/auth/service/AuthParentService.java similarity index 63% rename from src/main/java/com/project/growfit/domain/User/service/AuthParentService.java rename to src/main/java/com/project/growfit/domain/auth/service/AuthParentService.java index eb7671e..a68feb5 100644 --- a/src/main/java/com/project/growfit/domain/User/service/AuthParentService.java +++ b/src/main/java/com/project/growfit/domain/auth/service/AuthParentService.java @@ -1,9 +1,9 @@ -package com.project.growfit.domain.User.service; +package com.project.growfit.domain.auth.service; import com.google.zxing.WriterException; -import com.project.growfit.domain.User.dto.request.AuthParentRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthParentRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildQrCodeResponseDto; +import com.project.growfit.domain.auth.dto.response.ChildQrCodeResponseDto; public interface AuthParentService { ChildInfoResponseDto registerChild(AuthParentRequestDto request); diff --git a/src/main/java/com/project/growfit/domain/User/service/OauthService.java b/src/main/java/com/project/growfit/domain/auth/service/OauthService.java similarity index 78% rename from src/main/java/com/project/growfit/domain/User/service/OauthService.java rename to src/main/java/com/project/growfit/domain/auth/service/OauthService.java index 4c29ee1..a222a5a 100644 --- a/src/main/java/com/project/growfit/domain/User/service/OauthService.java +++ b/src/main/java/com/project/growfit/domain/auth/service/OauthService.java @@ -1,7 +1,7 @@ -package com.project.growfit.domain.User.service; +package com.project.growfit.domain.auth.service; -import com.project.growfit.domain.User.dto.request.ParentOAuthRequestDto; -import com.project.growfit.domain.User.dto.response.ParentResponseDto; +import com.project.growfit.domain.auth.dto.request.ParentOAuthRequestDto; +import com.project.growfit.domain.auth.dto.response.ParentResponseDto; import com.project.growfit.global.response.ResultResponse; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/project/growfit/domain/User/service/impl/AuthChildServiceImpl.java b/src/main/java/com/project/growfit/domain/auth/service/impl/AuthChildServiceImpl.java similarity index 95% rename from src/main/java/com/project/growfit/domain/User/service/impl/AuthChildServiceImpl.java rename to src/main/java/com/project/growfit/domain/auth/service/impl/AuthChildServiceImpl.java index 068d033..44f0718 100644 --- a/src/main/java/com/project/growfit/domain/User/service/impl/AuthChildServiceImpl.java +++ b/src/main/java/com/project/growfit/domain/auth/service/impl/AuthChildServiceImpl.java @@ -1,12 +1,12 @@ -package com.project.growfit.domain.User.service.impl; +package com.project.growfit.domain.auth.service.impl; -import com.project.growfit.domain.User.dto.request.AuthChildRequestDto; -import com.project.growfit.domain.User.dto.request.FindChildPasswordRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthChildRequestDto; +import com.project.growfit.domain.auth.dto.request.FindChildPasswordRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildResponseDto; +import com.project.growfit.domain.auth.dto.response.ChildResponseDto; import com.project.growfit.domain.User.entity.Child; import com.project.growfit.domain.User.repository.ChildRepository; -import com.project.growfit.domain.User.service.AuthChildService; +import com.project.growfit.domain.auth.service.AuthChildService; import com.project.growfit.global.auth.cookie.CookieService; import com.project.growfit.global.auth.jwt.JwtProvider; import com.project.growfit.global.auth.service.AuthenticatedUserProvider; diff --git a/src/main/java/com/project/growfit/domain/User/service/impl/AuthParentServiceImpl.java b/src/main/java/com/project/growfit/domain/auth/service/impl/AuthParentServiceImpl.java similarity index 93% rename from src/main/java/com/project/growfit/domain/User/service/impl/AuthParentServiceImpl.java rename to src/main/java/com/project/growfit/domain/auth/service/impl/AuthParentServiceImpl.java index f5c9ffa..61c8d83 100644 --- a/src/main/java/com/project/growfit/domain/User/service/impl/AuthParentServiceImpl.java +++ b/src/main/java/com/project/growfit/domain/auth/service/impl/AuthParentServiceImpl.java @@ -1,18 +1,18 @@ -package com.project.growfit.domain.User.service.impl; +package com.project.growfit.domain.auth.service.impl; import com.google.zxing.BarcodeFormat; import com.google.zxing.MultiFormatWriter; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; -import com.project.growfit.domain.User.dto.request.AuthParentRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthParentRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildQrCodeResponseDto; +import com.project.growfit.domain.auth.dto.response.ChildQrCodeResponseDto; import com.project.growfit.domain.User.entity.Child; import com.project.growfit.domain.User.entity.Parent; import com.project.growfit.domain.User.entity.ROLE; import com.project.growfit.domain.User.repository.ChildRepository; -import com.project.growfit.domain.User.service.AuthParentService; +import com.project.growfit.domain.auth.service.AuthParentService; import com.project.growfit.global.auth.service.AuthenticatedUserProvider; import com.project.growfit.global.exception.BusinessException; import com.project.growfit.global.exception.ErrorCode; @@ -107,6 +107,6 @@ protected Child createChild(AuthParentRequestDto request){ ROLE.fromString("CHILD")); } private static void updateNickname(AuthParentRequestDto request, Parent parent) { - parent.updateNickname(request.nickname()); + parent.updateInfo(request.nickname()); } } diff --git a/src/main/java/com/project/growfit/domain/User/service/impl/OauthServiceImpl.java b/src/main/java/com/project/growfit/domain/auth/service/impl/OauthServiceImpl.java similarity index 96% rename from src/main/java/com/project/growfit/domain/User/service/impl/OauthServiceImpl.java rename to src/main/java/com/project/growfit/domain/auth/service/impl/OauthServiceImpl.java index 7f0b5ee..6170c3e 100644 --- a/src/main/java/com/project/growfit/domain/User/service/impl/OauthServiceImpl.java +++ b/src/main/java/com/project/growfit/domain/auth/service/impl/OauthServiceImpl.java @@ -1,14 +1,14 @@ -package com.project.growfit.domain.User.service.impl; +package com.project.growfit.domain.auth.service.impl; import com.nimbusds.jose.shaded.gson.JsonElement; import com.nimbusds.jose.shaded.gson.JsonObject; import com.nimbusds.jose.shaded.gson.JsonParser; -import com.project.growfit.domain.User.dto.request.ParentOAuthRequestDto; -import com.project.growfit.domain.User.dto.response.ParentLoginResponseDto; -import com.project.growfit.domain.User.dto.response.ParentResponseDto; +import com.project.growfit.domain.auth.dto.request.ParentOAuthRequestDto; +import com.project.growfit.domain.auth.dto.response.ParentLoginResponseDto; +import com.project.growfit.domain.auth.dto.response.ParentResponseDto; import com.project.growfit.domain.User.entity.Parent; import com.project.growfit.domain.User.repository.ParentRepository; -import com.project.growfit.domain.User.service.OauthService; +import com.project.growfit.domain.auth.service.OauthService; import com.project.growfit.global.auth.cookie.CookieService; import com.project.growfit.global.auth.jwt.JwtProvider; import com.project.growfit.global.auth.service.AuthenticatedUserProvider; diff --git a/src/main/java/com/project/growfit/global/auth/jwt/JwtProvider.java b/src/main/java/com/project/growfit/global/auth/jwt/JwtProvider.java index 6d2d4ce..a0719ea 100644 --- a/src/main/java/com/project/growfit/global/auth/jwt/JwtProvider.java +++ b/src/main/java/com/project/growfit/global/auth/jwt/JwtProvider.java @@ -78,6 +78,19 @@ private String createJwt(String userId, String role, String loginType, Long expi return jwt; } + public void regenerateToken(String previousEmail, String newEmail, String role, String loginType, HttpServletResponse response + ) { + String newAccessToken = createAccessToken(newEmail, role, loginType); + String newRefreshToken = createRefreshToken(newEmail); + + tokenRedisRepository.deleteById(previousEmail); + tokenRedisRepository.save(new TokenRedis(newEmail, newAccessToken, newRefreshToken)); + + cookieService.saveAccessTokenToCookie(response, newAccessToken); + + log.info("[regenerateToken] 이메일 변경으로 인해 토큰 재발급 및 저장 완료 (old: {}, new: {})", previousEmail, newEmail); + } + public String getUserId(String token) { try { String userId = Jwts.parser().setSigningKey(secretKey).build() diff --git a/src/main/java/com/project/growfit/global/response/ResultCode.java b/src/main/java/com/project/growfit/global/response/ResultCode.java index bb108f6..74efde1 100644 --- a/src/main/java/com/project/growfit/global/response/ResultCode.java +++ b/src/main/java/com/project/growfit/global/response/ResultCode.java @@ -19,6 +19,7 @@ public enum ResultCode { LOGOUT_SUCCESS(HttpStatus.OK, "성공적으로 로그아웃하였습니다."), INFO_SUCCESS(HttpStatus.OK, "성공적으로 정보가 조회되었습니다."), + UPDATE_SUCCESS(HttpStatus.OK, "성공적으로 정보가 수정되었습니다."), //Diet STICKER_MARK_SUCCESS(HttpStatus.OK, "스티커가 성공적으로 등록되었습니다."), diff --git a/src/test/java/com/project/growfit/domain/User/controller/AuthParentControllerTest.java b/src/test/java/com/project/growfit/domain/User/controller/AuthParentControllerTest.java index d287b29..f5f8556 100644 --- a/src/test/java/com/project/growfit/domain/User/controller/AuthParentControllerTest.java +++ b/src/test/java/com/project/growfit/domain/User/controller/AuthParentControllerTest.java @@ -6,7 +6,7 @@ import com.project.growfit.domain.User.entity.Parent; import com.project.growfit.domain.User.entity.ROLE; import com.project.growfit.domain.User.repository.ParentRepository; -import com.project.growfit.domain.User.service.AuthParentService; +import com.project.growfit.domain.auth.service.AuthParentService; import com.project.growfit.global.auth.dto.CustomUserDetails; import com.project.growfit.global.config.SecurityConfig; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/project/growfit/domain/User/controller/OAuthControllerTest.java b/src/test/java/com/project/growfit/domain/User/controller/OAuthControllerTest.java index 04b7b1b..7134dbe 100644 --- a/src/test/java/com/project/growfit/domain/User/controller/OAuthControllerTest.java +++ b/src/test/java/com/project/growfit/domain/User/controller/OAuthControllerTest.java @@ -1,7 +1,7 @@ /* package com.project.growfit.domain.User.controller; -import com.project.growfit.domain.User.service.OauthService; +import com.project.growfit.domain.auth.service.OauthService; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/project/growfit/domain/User/controller/UserControllerTest.java b/src/test/java/com/project/growfit/domain/User/controller/UserControllerTest.java new file mode 100644 index 0000000..e22def5 --- /dev/null +++ b/src/test/java/com/project/growfit/domain/User/controller/UserControllerTest.java @@ -0,0 +1,115 @@ +package com.project.growfit.domain.User.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.project.growfit.domain.User.dto.request.ChildInfoRequestDto; +import com.project.growfit.domain.User.dto.request.ParentInfoRequestDto; +import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; +import com.project.growfit.domain.User.dto.response.ParentInfoResponseDto; +import com.project.growfit.domain.User.entity.ChildGender; +import com.project.growfit.domain.User.service.UserService; +import com.project.growfit.global.auth.jwt.JwtProvider; +import com.project.growfit.global.response.ResultCode; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +public class UserControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private UserService userService; + + @Autowired + private ObjectMapper objectMapper; + + @MockitoBean + private JwtProvider jwtProvider; + + @Test + @DisplayName("부모 정보 조회 API") + void getParentInfoTest() throws Exception { + ParentInfoResponseDto responseDto = new ParentInfoResponseDto( + "엄마", "profile.jpg", + new ChildInfoResponseDto(1L, "code123", "민준", ChildGender.MALE, 11, null) + ); + Mockito.when(userService.getParentInfo()).thenReturn(responseDto); + + mockMvc.perform(get("/api/parent")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value(ResultCode.INFO_SUCCESS.getStatus().value())) + .andExpect(jsonPath("$.data.nickname").value("엄마")); + } + + @Test + @DisplayName("아이 정보 조회 API") + void getChildInfoTest() throws Exception { + ChildInfoResponseDto dto = new ChildInfoResponseDto( + 1L, "codeABC", "지우", ChildGender.FEMALE, 10, null + ); + Mockito.when(userService.getChildInfo()).thenReturn(dto); + + mockMvc.perform(get("/api/child")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.child_name").value("지우")); + } + + @Test + @DisplayName("부모 정보 수정 API") + void updateParentInfoTest() throws Exception { + ParentInfoRequestDto requestDto = new ParentInfoRequestDto( + "새엄마", "new@example.com", "민준", 12, ChildGender.MALE, 140, 35 + ); + MockMultipartFile infoPart = new MockMultipartFile( + "info", + "info.json", + MediaType.APPLICATION_JSON_VALUE, + objectMapper.writeValueAsBytes(requestDto) + ); + + MockMultipartFile imagePart = new MockMultipartFile( + "image", + "profile.jpg", + MediaType.IMAGE_JPEG_VALUE, + "dummy-image-data".getBytes() + ); + + mockMvc.perform(multipart("/api/parent") + .file(infoPart) + .file(imagePart) + .with(request -> { + request.setMethod("PUT"); + return request; + })) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("성공적으로 정보가 수정되었습니다.")); + } + + @Test + @DisplayName("아이 정보 수정 API") + void updateChildInfoTest() throws Exception { + ChildInfoRequestDto requestDto = new ChildInfoRequestDto( + "짱이", "newpw1233", ChildGender.FEMALE, 13, 150L, 45L + ); + + mockMvc.perform(put("/api/child") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.message").value("성공적으로 정보가 수정되었습니다.")); + } +} \ No newline at end of file diff --git a/src/test/java/com/project/growfit/domain/User/repository/ChildRepositoryTest.java b/src/test/java/com/project/growfit/domain/User/repository/ChildRepositoryTest.java index 01af49b..611f797 100644 --- a/src/test/java/com/project/growfit/domain/User/repository/ChildRepositoryTest.java +++ b/src/test/java/com/project/growfit/domain/User/repository/ChildRepositoryTest.java @@ -28,17 +28,18 @@ public class ChildRepositoryTest { @BeforeEach void setUp() { - // 부모 저장 + childRepository.deleteAll(); + parentRepository.deleteAll(); + Parent parent = new Parent("parent@test.com", "부모", null, "kakao", "pid", ROLE.ROLE_PARENT); savedParent = parentRepository.save(parent); Child child = new Child("child123", "김아이", ChildGender.MALE, 10, 120, 50, "password123", ROLE.ROLE_CHILD); - savedParent.addChild(child); // 연관관계 설정 + savedParent.addChild(child); savedChild = childRepository.save(child); - child.updateCode("codeXYZ"); + savedChild.updateCode("codeXYZ"); } - @Test @DisplayName("[findByChildId 성공 테스트] 존재하는 Child 엔티티 반환") void testFindByChildId() { diff --git a/src/test/java/com/project/growfit/domain/User/service/impl/UserServiceImplTest.java b/src/test/java/com/project/growfit/domain/User/service/impl/UserServiceImplTest.java new file mode 100644 index 0000000..8ce7a72 --- /dev/null +++ b/src/test/java/com/project/growfit/domain/User/service/impl/UserServiceImplTest.java @@ -0,0 +1,121 @@ +package com.project.growfit.domain.User.service.impl; + +import com.project.growfit.domain.User.dto.request.ChildInfoRequestDto; +import com.project.growfit.domain.User.dto.request.ParentInfoRequestDto; +import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; +import com.project.growfit.domain.User.dto.response.ParentInfoResponseDto; +import com.project.growfit.domain.User.entity.Child; +import com.project.growfit.domain.User.entity.ChildGender; +import com.project.growfit.domain.User.entity.Parent; +import com.project.growfit.domain.User.entity.ROLE; +import com.project.growfit.global.auth.jwt.JwtProvider; +import com.project.growfit.global.auth.service.AuthenticatedUserProvider; +import com.project.growfit.global.s3.service.S3UploadService; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.multipart.MultipartFile; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +public class UserServiceImplTest { + + @InjectMocks + private UserServiceImpl userService; + + @Mock + private AuthenticatedUserProvider userProvider; + + @Mock + private S3UploadService s3UploadService; + + @Mock + private JwtProvider jwtProvider; + + @Mock + private PasswordEncoder passwordEncoder; + + @Mock + private HttpServletResponse response; + + private Parent mockParent; + private Child mockChild; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + + mockChild = new Child("child123", "지우", ChildGender.FEMALE, 10, 130, 30, "encodedPw", ROLE.ROLE_CHILD); + mockParent = new Parent("parent@example.com", "엄마", "photo.jpg", "kakao", "pid", ROLE.ROLE_PARENT); + mockParent.addChild(mockChild); + } + + @Test + @DisplayName("부모 정보 조회 성공") + void testGetParentInfo() { + when(userProvider.getAuthenticatedParent()).thenReturn(mockParent); + + ParentInfoResponseDto result = userService.getParentInfo(); + + assertThat(result.nickname()).isEqualTo("엄마"); + assertThat(result.child().child_name()).isEqualTo("지우"); + } + + @Test + @DisplayName("아이 정보 조회 성공") + void testGetChildInfo() { + when(userProvider.getAuthenticatedChild()).thenReturn(mockChild); + + ChildInfoResponseDto result = userService.getChildInfo(); + + assertThat(result.child_name()).isEqualTo("지우"); + } + + @Test + @DisplayName("부모 정보 수정 - 이메일 변경 포함") + void testUpdateParentInfo_withEmailChange() { + when(userProvider.getAuthenticatedParent()).thenReturn(mockParent); + when(s3UploadService.saveFile(any(MultipartFile.class), anyString())) + .thenReturn("https://s3.com/profile.jpg"); + + ParentInfoRequestDto request = new ParentInfoRequestDto( + "새엄마", "new@example.com", "지우", 11, ChildGender.FEMALE, 135, 32 + ); + MultipartFile mockImage = new MockMultipartFile("image", "test.jpg", "image/jpeg", "dummy".getBytes()); + + userService.updateParentInfo(request, mockImage, response); + + verify(jwtProvider).regenerateToken( + eq("parent@example.com"), + eq("new@example.com"), + eq("ROLE_PARENT"), + eq("SOCIAL_KAKAO"), + eq(response) + ); + } + + @Test + @DisplayName("아이 정보 수정 - 비밀번호 포함") + void testUpdateChildInfo_withPassword() { + when(userProvider.getAuthenticatedChild()).thenReturn(mockChild); + when(passwordEncoder.matches(anyString(), anyString())).thenReturn(false); + when(passwordEncoder.encode(anyString())).thenReturn("encodedNewPw"); + + ChildInfoRequestDto request = new ChildInfoRequestDto( + "지우업", "새비번123", ChildGender.FEMALE, 12, 140L, 35L + ); + + userService.updateChildInfo(request); + + verify(passwordEncoder).encode("새비번123"); + assertThat(mockChild.getNickname()).isEqualTo("지우업"); + assertThat(mockChild.getLatestBodyInfo().getHeight()).isEqualTo(140L); + } +} \ No newline at end of file diff --git a/src/test/java/com/project/growfit/domain/auth/controller/AuthChildControllerTest.java b/src/test/java/com/project/growfit/domain/auth/controller/AuthChildControllerTest.java index 5347fb4..009a884 100644 --- a/src/test/java/com/project/growfit/domain/auth/controller/AuthChildControllerTest.java +++ b/src/test/java/com/project/growfit/domain/auth/controller/AuthChildControllerTest.java @@ -2,10 +2,10 @@ package com.project.growfit.domain.auth.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import com.project.growfit.domain.User.controller.AuthChildController; -import com.project.growfit.domain.User.dto.request.AuthChildRequestDto; -import com.project.growfit.domain.User.service.AuthChildService; -import com.project.growfit.domain.User.service.impl.AuthChildServiceImpl; +import com.project.growfit.domain.auth.controller.AuthChildController; +import com.project.growfit.domain.auth.dto.request.AuthChildRequestDto; +import com.project.growfit.domain.auth.service.AuthChildService; +import com.project.growfit.domain.auth.service.impl.AuthChildServiceImpl; import com.project.growfit.global.response.ResultCode; import com.project.growfit.global.response.ResultResponse; import jakarta.servlet.http.HttpServletResponse; diff --git a/src/test/java/com/project/growfit/domain/auth/controller/AuthParentControllerTest.java b/src/test/java/com/project/growfit/domain/auth/controller/AuthParentControllerTest.java index b8ca980..6047f38 100644 --- a/src/test/java/com/project/growfit/domain/auth/controller/AuthParentControllerTest.java +++ b/src/test/java/com/project/growfit/domain/auth/controller/AuthParentControllerTest.java @@ -2,11 +2,11 @@ package com.project.growfit.domain.auth.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import com.project.growfit.domain.User.controller.AuthParentController; +import com.project.growfit.domain.auth.controller.AuthParentController; import com.project.growfit.domain.User.entity.Parent; import com.project.growfit.domain.User.entity.ROLE; import com.project.growfit.domain.User.repository.ParentRepository; -import com.project.growfit.domain.User.service.AuthParentService; +import com.project.growfit.domain.auth.service.AuthParentService; import com.project.growfit.global.auth.dto.CustomUserDetails; import com.project.growfit.global.config.SecurityConfig; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/project/growfit/domain/auth/controller/OAuthControllerTest.java b/src/test/java/com/project/growfit/domain/auth/controller/OAuthControllerTest.java index af71061..4d4bb4c 100644 --- a/src/test/java/com/project/growfit/domain/auth/controller/OAuthControllerTest.java +++ b/src/test/java/com/project/growfit/domain/auth/controller/OAuthControllerTest.java @@ -1,8 +1,8 @@ /* package com.project.growfit.domain.auth.controller; -import com.project.growfit.domain.User.controller.OAuthController; -import com.project.growfit.domain.User.service.OauthService; +import com.project.growfit.domain.auth.controller.OAuthController; +import com.project.growfit.domain.auth.service.OauthService; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/project/growfit/domain/User/service/impl/AuthChildServiceImplTest.java b/src/test/java/com/project/growfit/domain/auth/service/impl/AuthChildServiceImplTest.java similarity index 96% rename from src/test/java/com/project/growfit/domain/User/service/impl/AuthChildServiceImplTest.java rename to src/test/java/com/project/growfit/domain/auth/service/impl/AuthChildServiceImplTest.java index 5eb8b77..7d89f9e 100644 --- a/src/test/java/com/project/growfit/domain/User/service/impl/AuthChildServiceImplTest.java +++ b/src/test/java/com/project/growfit/domain/auth/service/impl/AuthChildServiceImplTest.java @@ -1,9 +1,9 @@ -package com.project.growfit.domain.User.service.impl; +package com.project.growfit.domain.auth.service.impl; -import com.project.growfit.domain.User.dto.request.AuthChildRequestDto; -import com.project.growfit.domain.User.dto.request.FindChildPasswordRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthChildRequestDto; +import com.project.growfit.domain.auth.dto.request.FindChildPasswordRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildResponseDto; +import com.project.growfit.domain.auth.dto.response.ChildResponseDto; import com.project.growfit.domain.User.entity.Child; import com.project.growfit.domain.User.entity.ROLE; import com.project.growfit.domain.User.repository.ChildRepository; @@ -15,8 +15,6 @@ import com.project.growfit.global.exception.ErrorCode; import com.project.growfit.global.redis.entity.TokenRedis; import com.project.growfit.global.redis.repository.TokenRedisRepository; -import com.project.growfit.global.response.ResultCode; -import com.project.growfit.global.response.ResultResponse; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/com/project/growfit/domain/User/service/impl/AuthParentServiceImplTest.java b/src/test/java/com/project/growfit/domain/auth/service/impl/AuthParentServiceImplTest.java similarity index 95% rename from src/test/java/com/project/growfit/domain/User/service/impl/AuthParentServiceImplTest.java rename to src/test/java/com/project/growfit/domain/auth/service/impl/AuthParentServiceImplTest.java index 0ba7872..daf9eb8 100644 --- a/src/test/java/com/project/growfit/domain/User/service/impl/AuthParentServiceImplTest.java +++ b/src/test/java/com/project/growfit/domain/auth/service/impl/AuthParentServiceImplTest.java @@ -1,8 +1,8 @@ -package com.project.growfit.domain.User.service.impl; +package com.project.growfit.domain.auth.service.impl; -import com.project.growfit.domain.User.dto.request.AuthParentRequestDto; +import com.project.growfit.domain.auth.dto.request.AuthParentRequestDto; import com.project.growfit.domain.User.dto.response.ChildInfoResponseDto; -import com.project.growfit.domain.User.dto.response.ChildQrCodeResponseDto; +import com.project.growfit.domain.auth.dto.response.ChildQrCodeResponseDto; import com.project.growfit.domain.User.entity.Child; import com.project.growfit.domain.User.entity.Parent; import com.project.growfit.domain.User.entity.ROLE; @@ -20,8 +20,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.Optional; - import static com.project.growfit.domain.User.entity.ChildGender.MALE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/src/test/java/com/project/growfit/domain/User/service/impl/OauthServiceImplTest.java b/src/test/java/com/project/growfit/domain/auth/service/impl/OauthServiceImplTest.java similarity index 95% rename from src/test/java/com/project/growfit/domain/User/service/impl/OauthServiceImplTest.java rename to src/test/java/com/project/growfit/domain/auth/service/impl/OauthServiceImplTest.java index ecc89e9..daba53b 100644 --- a/src/test/java/com/project/growfit/domain/User/service/impl/OauthServiceImplTest.java +++ b/src/test/java/com/project/growfit/domain/auth/service/impl/OauthServiceImplTest.java @@ -1,7 +1,7 @@ -package com.project.growfit.domain.User.service.impl; +package com.project.growfit.domain.auth.service.impl; -import com.project.growfit.domain.User.dto.request.ParentOAuthRequestDto; -import com.project.growfit.domain.User.dto.response.ParentResponseDto; +import com.project.growfit.domain.auth.dto.request.ParentOAuthRequestDto; +import com.project.growfit.domain.auth.dto.response.ParentResponseDto; import com.project.growfit.domain.User.entity.Parent; import com.project.growfit.domain.User.entity.ROLE; import com.project.growfit.domain.User.repository.ParentRepository; diff --git a/src/test/java/com/project/growfit/global/response/ErrorResponseTest.java b/src/test/java/com/project/growfit/global/response/ErrorResponseTest.java index 236f1c1..660b2eb 100644 --- a/src/test/java/com/project/growfit/global/response/ErrorResponseTest.java +++ b/src/test/java/com/project/growfit/global/response/ErrorResponseTest.java @@ -23,7 +23,7 @@ void createErrorResponseWithCodeOnly() { // then assertThat(response.getStatus()).isEqualTo(500); - assertThat(response.getMessage()).isEqualTo("internal server error"); + assertThat(response.getMessage()).isEqualTo("서버 내부 오류가 발생했습니다."); assertThat(response.getErrors()).isEmpty(); } @@ -42,7 +42,7 @@ void createErrorResponseWithFieldErrors() { // then assertThat(response.getStatus()).isEqualTo(400); - assertThat(response.getMessage()).isEqualTo("invalid input type"); + assertThat(response.getMessage()).isEqualTo("잘못된 입력 값입니다."); assertThat(response.getErrors()).hasSize(2); assertThat(response.getErrors().get(0).getField()).isEqualTo("field1"); assertThat(response.getErrors().get(0).getValue()).isEqualTo("value1"); @@ -63,7 +63,7 @@ void createErrorResponseWithMethodArgumentTypeMismatchException() { // then assertThat(response.getStatus()).isEqualTo(400); - assertThat(response.getMessage()).isEqualTo("invalid type value"); + assertThat(response.getMessage()).isEqualTo("잘못된 타입 값입니다."); assertThat(response.getErrors()).isNotEmpty(); } } \ No newline at end of file