From 45c537ba8b48f0c91801f6ae9705c8dd81ba1c43 Mon Sep 17 00:00:00 2001 From: limchangin Date: Wed, 16 Jul 2025 16:07:23 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 13 +++++++++++++ .../domain/user/dto/PasswordUpdateDto.java | 18 ++++++++++++++++++ .../trainus/domain/user/entity/User.java | 3 +++ .../domain/user/service/UserService.java | 19 +++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 src/main/java/com/threestar/trainus/domain/user/dto/PasswordUpdateDto.java diff --git a/src/main/java/com/threestar/trainus/domain/user/controller/UserController.java b/src/main/java/com/threestar/trainus/domain/user/controller/UserController.java index ab09966..177e8af 100644 --- a/src/main/java/com/threestar/trainus/domain/user/controller/UserController.java +++ b/src/main/java/com/threestar/trainus/domain/user/controller/UserController.java @@ -2,6 +2,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -13,10 +14,12 @@ import com.threestar.trainus.domain.user.dto.LoginRequestDto; import com.threestar.trainus.domain.user.dto.LoginResponseDto; import com.threestar.trainus.domain.user.dto.NicknameCheckRequestDto; +import com.threestar.trainus.domain.user.dto.PasswordUpdateDto; import com.threestar.trainus.domain.user.dto.SignupRequestDto; import com.threestar.trainus.domain.user.dto.SignupResponseDto; import com.threestar.trainus.domain.user.service.EmailVerificationService; import com.threestar.trainus.domain.user.service.UserService; +import com.threestar.trainus.global.annotation.LoginUser; import com.threestar.trainus.global.unit.BaseResponse; import io.swagger.v3.oas.annotations.Operation; @@ -87,4 +90,14 @@ public ResponseEntity> confirmVerificationCode( emailVerificationService.verifyCode(request.email(), request.verificationCode()); return BaseResponse.ok("이메일 인증이 완료되었습니다.", null, HttpStatus.OK); } + + @PatchMapping("/password") + @Operation(summary = "비밀번호 변경 api") + public ResponseEntity> updatePassword( + @Valid @RequestBody PasswordUpdateDto request, + @LoginUser Long loginUserId + ) { + userService.updatePassword(request, loginUserId); + return BaseResponse.ok("비밀번호 변경이 완료되었습니다.", null, HttpStatus.OK); + } } diff --git a/src/main/java/com/threestar/trainus/domain/user/dto/PasswordUpdateDto.java b/src/main/java/com/threestar/trainus/domain/user/dto/PasswordUpdateDto.java new file mode 100644 index 0000000..167b97b --- /dev/null +++ b/src/main/java/com/threestar/trainus/domain/user/dto/PasswordUpdateDto.java @@ -0,0 +1,18 @@ +package com.threestar.trainus.domain.user.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record PasswordUpdateDto( + + @NotBlank(message = "현재 비밀번호는 필수입니다.") + String currentPassword, + + @NotBlank(message = "새 비밀번호는 필수입니다.") + @Size(min = 8, max = 20, message = "비밀번호는 8자 이상 20자 이하입니다.") + String newPassword, + + @NotBlank(message = "새 비밀번호 확인은 필수입니다.") + String confirmPassword +) { +} diff --git a/src/main/java/com/threestar/trainus/domain/user/entity/User.java b/src/main/java/com/threestar/trainus/domain/user/entity/User.java index 387f18a..20b1e0f 100644 --- a/src/main/java/com/threestar/trainus/domain/user/entity/User.java +++ b/src/main/java/com/threestar/trainus/domain/user/entity/User.java @@ -62,4 +62,7 @@ public class User extends BaseDateEntity { @OneToMany(mappedBy = "user") private List userCoupons = new ArrayList<>(); + public void updatePassword(String newPassword) { + this.password = newPassword; + } } diff --git a/src/main/java/com/threestar/trainus/domain/user/service/UserService.java b/src/main/java/com/threestar/trainus/domain/user/service/UserService.java index 00df67a..8b88b10 100644 --- a/src/main/java/com/threestar/trainus/domain/user/service/UserService.java +++ b/src/main/java/com/threestar/trainus/domain/user/service/UserService.java @@ -11,6 +11,7 @@ import com.threestar.trainus.domain.profile.service.ProfileFacadeService; import com.threestar.trainus.domain.user.dto.LoginRequestDto; import com.threestar.trainus.domain.user.dto.LoginResponseDto; +import com.threestar.trainus.domain.user.dto.PasswordUpdateDto; import com.threestar.trainus.domain.user.dto.SignupRequestDto; import com.threestar.trainus.domain.user.dto.SignupResponseDto; import com.threestar.trainus.domain.user.entity.User; @@ -119,4 +120,22 @@ public User getAdminUser(Long userId) { return user; } + public void updatePassword(PasswordUpdateDto request, Long userId) { + //새 비밀번호와 새 비밀번호 확인끼리의 검증 + if (!request.newPassword().equals(request.confirmPassword())) { + throw new BusinessException(ErrorCode.INVALID_REQUEST_DATA); + } + + User user = getUserById(userId); + //유저의 현재 비밀번호 검증 + if (!passwordEncoder.matches(request.currentPassword(), user.getPassword())) { + throw new BusinessException(ErrorCode.INVALID_REQUEST_DATA); + } + + String encordedNewPassword = passwordEncoder.encode(request.newPassword()); + + user.updatePassword(encordedNewPassword); + + userRepository.save(user); + } } From 1f3c66020571b24d9694c61925d1ac15de85fbcd Mon Sep 17 00:00:00 2001 From: limchangin Date: Wed, 16 Jul 2025 16:40:38 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98=EC=A0=95,=20=EC=9E=90?= =?UTF-8?q?=EA=B8=B0=EC=86=8C=EA=B0=9C=20=EC=88=98=EC=A0=95=20api=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/LessonApplicationResponseDto.java | 1 + .../lesson/admin/dto/ParticipantDto.java | 1 + .../admin/mapper/LessonApplicationMapper.java | 1 + .../admin/mapper/LessonParticipantMapper.java | 1 + .../profile/controller/ProfileController.java | 33 +- ...estDto.java => ImageUpdateRequestDto.java} | 9 +- .../profile/dto/IntroUpdateRequestDto.java | 10 + .../profile/dto/IntroUpdateResponseDto.java | 8 + .../domain/profile/entity/Profile.java | 5 +- .../domain/profile/mapper/ProfileMapper.java | 18 + .../profile/service/ProfileFacadeService.java | 14 +- .../profile/service/ProfileService.java | 25 +- .../global/config/MockDataInitializer.java | 468 +++++++++--------- 13 files changed, 333 insertions(+), 261 deletions(-) rename src/main/java/com/threestar/trainus/domain/profile/dto/{ProfileUpdateRequestDto.java => ImageUpdateRequestDto.java} (64%) create mode 100644 src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateRequestDto.java create mode 100644 src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateResponseDto.java diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java index 0929982..f8832a8 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import com.threestar.trainus.domain.lesson.admin.entity.ApplicationStatus; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; import lombok.Builder; diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java index 21f3062..8824894 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java @@ -2,6 +2,7 @@ import java.time.LocalDateTime; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; import lombok.Builder; diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java index 8ca5746..7b073be 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java @@ -5,6 +5,7 @@ import com.threestar.trainus.domain.lesson.admin.dto.LessonApplicationListResponseDto; import com.threestar.trainus.domain.lesson.admin.dto.LessonApplicationResponseDto; import com.threestar.trainus.domain.lesson.admin.entity.LessonApplication; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; public class LessonApplicationMapper { diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java index 075208b..d1e7a57 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java @@ -5,6 +5,7 @@ import com.threestar.trainus.domain.lesson.admin.dto.ParticipantDto; import com.threestar.trainus.domain.lesson.admin.dto.ParticipantListResponseDto; import com.threestar.trainus.domain.lesson.admin.entity.LessonApplication; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; public class LessonParticipantMapper { diff --git a/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java b/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java index fca1da6..788c801 100644 --- a/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java +++ b/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java @@ -9,15 +9,17 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.threestar.trainus.domain.profile.dto.IntroUpdateRequestDto; import com.threestar.trainus.domain.profile.dto.ProfileDetailResponseDto; -import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; -import com.threestar.trainus.domain.profile.dto.ProfileUpdateRequestDto; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; +import com.threestar.trainus.domain.profile.dto.ImageUpdateRequestDto; +import com.threestar.trainus.domain.profile.dto.IntroUpdateResponseDto; import com.threestar.trainus.domain.profile.service.ProfileFacadeService; +import com.threestar.trainus.global.annotation.LoginUser; import com.threestar.trainus.global.unit.BaseResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpSession; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -38,14 +40,23 @@ public ResponseEntity> getProfileDetail( return BaseResponse.ok("프로필 상세 조회가 완료되었습니다.", response, HttpStatus.OK); } - @PatchMapping - @Operation(summary = "유저 프로필 수정 api") - public ResponseEntity> updateProfile( - @Valid @RequestBody ProfileUpdateRequestDto requestDto, - HttpSession session + @PatchMapping("/image") + @Operation(summary = "유저 프로필 이미지 수정 api") + public ResponseEntity> updateProfileImage( + @Valid @RequestBody ImageUpdateRequestDto requestDto, + @LoginUser Long loginUserId ) { - Long userId = (Long)session.getAttribute("LOGIN_USER"); - ProfileResponseDto response = facadeService.updateProfile(userId, requestDto); - return BaseResponse.ok("프로필 수정이 완료되었습니다.", response, HttpStatus.OK); + ImageUpdateResponseDto response = facadeService.updateProfileImage(loginUserId, requestDto); + return BaseResponse.ok("프로필 이미지 수정이 완료되었습니다.", response, HttpStatus.OK); + } + + @PatchMapping("/intro") + @Operation(summary = "유저 프로필 자기소개 수정 api") + public ResponseEntity> updateProfileIntro( + @Valid @RequestBody IntroUpdateRequestDto requestDto, + @LoginUser Long loginUserId + ) { + IntroUpdateResponseDto response = facadeService.updateProfileIntro(loginUserId, requestDto); + return BaseResponse.ok("프로필 자기소개 수정이 완료되었습니다.", response, HttpStatus.OK); } } diff --git a/src/main/java/com/threestar/trainus/domain/profile/dto/ProfileUpdateRequestDto.java b/src/main/java/com/threestar/trainus/domain/profile/dto/ImageUpdateRequestDto.java similarity index 64% rename from src/main/java/com/threestar/trainus/domain/profile/dto/ProfileUpdateRequestDto.java rename to src/main/java/com/threestar/trainus/domain/profile/dto/ImageUpdateRequestDto.java index e332a49..7c10d6d 100644 --- a/src/main/java/com/threestar/trainus/domain/profile/dto/ProfileUpdateRequestDto.java +++ b/src/main/java/com/threestar/trainus/domain/profile/dto/ImageUpdateRequestDto.java @@ -1,20 +1,15 @@ package com.threestar.trainus.domain.profile.dto; -import org.hibernate.validator.constraints.URL; - import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; -public record ProfileUpdateRequestDto( +public record ImageUpdateRequestDto( @Size(max = 2048, message = "프로필 이미지 URL은 2048자 이하여야 합니다.") @Pattern( regexp = "^$|^https?://[\\w\\-._~:/?#\\[\\]@!$&'()*+,;=%]+$", message = "올바른 URL 형식이어야 합니다." ) - String profileImage, - - @Size(max = 255, message = "자기소개는 255자 이하여야 합니다.") - String intro + String profileImage ) { } diff --git a/src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateRequestDto.java b/src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateRequestDto.java new file mode 100644 index 0000000..dd1717f --- /dev/null +++ b/src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateRequestDto.java @@ -0,0 +1,10 @@ +package com.threestar.trainus.domain.profile.dto; + +import jakarta.validation.constraints.Size; + +public record IntroUpdateRequestDto( + + @Size(max = 255, message = "자기소개는 255자 이하여야 합니다.") + String intro +) { +} diff --git a/src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateResponseDto.java b/src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateResponseDto.java new file mode 100644 index 0000000..a58f6a7 --- /dev/null +++ b/src/main/java/com/threestar/trainus/domain/profile/dto/IntroUpdateResponseDto.java @@ -0,0 +1,8 @@ +package com.threestar.trainus.domain.profile.dto; + +public record IntroUpdateResponseDto( + Long userId, + String nickname, + String intro +) { +} diff --git a/src/main/java/com/threestar/trainus/domain/profile/entity/Profile.java b/src/main/java/com/threestar/trainus/domain/profile/entity/Profile.java index 334c370..76c7162 100644 --- a/src/main/java/com/threestar/trainus/domain/profile/entity/Profile.java +++ b/src/main/java/com/threestar/trainus/domain/profile/entity/Profile.java @@ -39,8 +39,11 @@ public class Profile { @Column(length = 255) private String intro; - public void updateProfile(String profileImage, String intro) { + public void updateProfileImage(String profileImage) { this.profileImage = profileImage; + } + + public void updateProfileIntro(String intro) { this.intro = intro; } } diff --git a/src/main/java/com/threestar/trainus/domain/profile/mapper/ProfileMapper.java b/src/main/java/com/threestar/trainus/domain/profile/mapper/ProfileMapper.java index 0c5d227..38dab16 100644 --- a/src/main/java/com/threestar/trainus/domain/profile/mapper/ProfileMapper.java +++ b/src/main/java/com/threestar/trainus/domain/profile/mapper/ProfileMapper.java @@ -1,5 +1,7 @@ package com.threestar.trainus.domain.profile.mapper; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; +import com.threestar.trainus.domain.profile.dto.IntroUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; import com.threestar.trainus.domain.profile.entity.Profile; import com.threestar.trainus.domain.user.entity.User; @@ -19,6 +21,22 @@ public static ProfileResponseDto toResponseDto(Profile profile, User user) { ); } + public static ImageUpdateResponseDto toImageResponseDto(Profile profile, User user) { + return new ImageUpdateResponseDto( + user.getId(), + user.getNickname(), + profile.getProfileImage() + ); + } + + public static IntroUpdateResponseDto toIntroResponseDto(Profile profile, User user) { + return new IntroUpdateResponseDto( + user.getId(), + user.getNickname(), + profile.getIntro() + ); + } + public static Profile toDefaultEntity(User user) { return Profile.builder() .user(user) diff --git a/src/main/java/com/threestar/trainus/domain/profile/service/ProfileFacadeService.java b/src/main/java/com/threestar/trainus/domain/profile/service/ProfileFacadeService.java index bb26b8d..0d01c78 100644 --- a/src/main/java/com/threestar/trainus/domain/profile/service/ProfileFacadeService.java +++ b/src/main/java/com/threestar/trainus/domain/profile/service/ProfileFacadeService.java @@ -5,9 +5,12 @@ import com.threestar.trainus.domain.metadata.dto.ProfileMetadataResponseDto; import com.threestar.trainus.domain.metadata.service.ProfileMetadataService; +import com.threestar.trainus.domain.profile.dto.IntroUpdateRequestDto; import com.threestar.trainus.domain.profile.dto.ProfileDetailResponseDto; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; +import com.threestar.trainus.domain.profile.dto.ImageUpdateRequestDto; +import com.threestar.trainus.domain.profile.dto.IntroUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; -import com.threestar.trainus.domain.profile.dto.ProfileUpdateRequestDto; import com.threestar.trainus.domain.profile.mapper.ProfileDetailMapper; import com.threestar.trainus.domain.user.entity.User; @@ -31,8 +34,13 @@ public ProfileDetailResponseDto getProfileDetail(Long userId) { //프로필 수정 @Transactional - public ProfileResponseDto updateProfile(Long userId, ProfileUpdateRequestDto requestDto) { - return profileService.updateProfile(userId, requestDto); + public ImageUpdateResponseDto updateProfileImage(Long userId, ImageUpdateRequestDto requestDto) { + return profileService.updateProfileImage(userId, requestDto); + } + + @Transactional + public IntroUpdateResponseDto updateProfileIntro(Long userId, IntroUpdateRequestDto requestDto) { + return profileService.updateProfileIntro(userId, requestDto); } //회원가입 시 프로필,메타데이터 디폴트로 생성. diff --git a/src/main/java/com/threestar/trainus/domain/profile/service/ProfileService.java b/src/main/java/com/threestar/trainus/domain/profile/service/ProfileService.java index e575be4..024821a 100644 --- a/src/main/java/com/threestar/trainus/domain/profile/service/ProfileService.java +++ b/src/main/java/com/threestar/trainus/domain/profile/service/ProfileService.java @@ -3,14 +3,16 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.threestar.trainus.domain.profile.dto.IntroUpdateRequestDto; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; +import com.threestar.trainus.domain.profile.dto.ImageUpdateRequestDto; +import com.threestar.trainus.domain.profile.dto.IntroUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; -import com.threestar.trainus.domain.profile.dto.ProfileUpdateRequestDto; import com.threestar.trainus.domain.profile.entity.Profile; import com.threestar.trainus.domain.profile.mapper.ProfileMapper; import com.threestar.trainus.domain.profile.repository.ProfileRepository; import com.threestar.trainus.domain.user.entity.User; import com.threestar.trainus.domain.user.repository.UserRepository; -import com.threestar.trainus.domain.user.service.UserService; import com.threestar.trainus.global.exception.domain.ErrorCode; import com.threestar.trainus.global.exception.handler.BusinessException; @@ -41,16 +43,29 @@ public ProfileResponseDto getProfile(Long userId) { } @Transactional - public ProfileResponseDto updateProfile(Long userId, ProfileUpdateRequestDto requestDto) { + public ImageUpdateResponseDto updateProfileImage(Long userId, ImageUpdateRequestDto requestDto) { User user = userRepository.findById(userId) .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); Profile profile = profileRepository.findByUserId(userId) .orElseThrow(() -> new BusinessException(ErrorCode.PROFILE_NOT_FOUND)); - profile.updateProfile(requestDto.profileImage(), requestDto.intro()); + profile.updateProfileImage(requestDto.profileImage()); - return ProfileMapper.toResponseDto(profile, user); + return ProfileMapper.toImageResponseDto(profile, user); + } + + @Transactional + public IntroUpdateResponseDto updateProfileIntro(Long userId, IntroUpdateRequestDto requestDto) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); + + Profile profile = profileRepository.findByUserId(userId) + .orElseThrow(() -> new BusinessException(ErrorCode.PROFILE_NOT_FOUND)); + + profile.updateProfileIntro(requestDto.intro()); + + return ProfileMapper.toIntroResponseDto(profile, user); } } diff --git a/src/main/java/com/threestar/trainus/global/config/MockDataInitializer.java b/src/main/java/com/threestar/trainus/global/config/MockDataInitializer.java index 5a0fef1..4977310 100644 --- a/src/main/java/com/threestar/trainus/global/config/MockDataInitializer.java +++ b/src/main/java/com/threestar/trainus/global/config/MockDataInitializer.java @@ -1,234 +1,234 @@ -package com.threestar.trainus.global.config; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import org.springframework.boot.CommandLineRunner; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import com.threestar.trainus.domain.lesson.admin.entity.Category; -import com.threestar.trainus.domain.lesson.admin.entity.Lesson; -import com.threestar.trainus.domain.lesson.admin.repository.LessonRepository; -import com.threestar.trainus.domain.metadata.entity.ProfileMetadata; -import com.threestar.trainus.domain.metadata.mapper.ProfileMetadataMapper; -import com.threestar.trainus.domain.metadata.repository.ProfileMetadataRepository; -import com.threestar.trainus.domain.profile.entity.Profile; -import com.threestar.trainus.domain.profile.mapper.ProfileMapper; -import com.threestar.trainus.domain.profile.repository.ProfileRepository; -import com.threestar.trainus.domain.review.entity.Review; -import com.threestar.trainus.domain.review.repository.ReviewRepository; -import com.threestar.trainus.domain.user.entity.User; -import com.threestar.trainus.domain.user.entity.UserRole; -import com.threestar.trainus.domain.user.repository.UserRepository; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Component -@org.springframework.context.annotation.Profile("dev") // dev 프로필에서만 실행 -@RequiredArgsConstructor -public class MockDataInitializer implements CommandLineRunner { - - private final UserRepository userRepository; - private final ProfileRepository profileRepository; - private final ProfileMetadataRepository profileMetadataRepository; - private final LessonRepository lessonRepository; - private final ReviewRepository reviewRepository; - private final PasswordEncoder passwordEncoder; - - private final Random random = new Random(); - - @Override - @Transactional - public void run(String... args) throws Exception { - if (userRepository.count() > 0) { - log.info("데이터가 이미 존재합니다. Mock 데이터 생성을 건너뜁니다."); - return; - } - - log.info("Mock 데이터 생성 시작..."); - - // 1. 강사 유저 생성 (15명) - List instructors = createInstructors(); - - // 2. 수강생 유저 생성 (50명) - List students = createStudents(); - - // 3. 각 강사별로 레슨 생성 - List lessons = createLessons(instructors); - - // 4. 리뷰 생성 (ProfileMetadata 업데이트) - createReviews(instructors, students, lessons); - - log.info("Mock 데이터 생성 완료!"); - log.info("생성된 데이터: 강사 {}명, 수강생 {}명, 레슨 {}개", - instructors.size(), students.size(), lessons.size()); - } - - private List createInstructors() { - List instructors = new ArrayList<>(); - String[] instructorNames = { - "헬스왕김철수", "요가마스터", "필라테스여신", "러닝코치박", "수영선수이", - "테니스프로", "복싱챔피언", "클라이밍킹", "골프레슨프로", "댄스퀸", - "크로스핏코치", "배드민턴고수", "탁구선수", "농구코치", "축구감독" - }; - - for (int i = 0; i < instructorNames.length; i++) { - User instructor = User.builder() - .email("instructor" + (i + 1) + "@test.com") - .password(passwordEncoder.encode("password123")) - .nickname(instructorNames[i]) - .role(UserRole.USER) - .build(); - - User savedInstructor = userRepository.save(instructor); - - // Profile 생성 - Profile profile = ProfileMapper.toDefaultEntity(savedInstructor); - profile.updateProfile( - "https://example.com/instructor" + (i + 1) + ".jpg", - instructorNames[i] + "입니다. 최고의 레슨을 제공합니다!" - ); - profileRepository.save(profile); - - // ProfileMetadata 생성 (랭킹용 데이터) - ProfileMetadata metadata = ProfileMetadata.builder() - .user(savedInstructor) - .reviewCount(generateReviewCount()) - .rating(generateRating()) - .build(); - profileMetadataRepository.save(metadata); - - instructors.add(savedInstructor); - } - - return instructors; - } - - private List createStudents() { - List students = new ArrayList<>(); - - for (int i = 0; i < 50; i++) { - User student = User.builder() - .email("student" + (i + 1) + "@test.com") - .password(passwordEncoder.encode("password123")) - .nickname("수강생" + (i + 1)) - .role(UserRole.USER) - .build(); - - User savedStudent = userRepository.save(student); - - // Profile 생성 - Profile profile = ProfileMapper.toDefaultEntity(savedStudent); - profile.updateProfile( - "https://example.com/student" + (i + 1) + ".jpg", - "운동을 열심히 하는 수강생입니다!" - ); - profileRepository.save(profile); - - // 수강생은 메타데이터 기본값 - ProfileMetadata metadata = ProfileMetadataMapper.toDefaultEntity(savedStudent); - profileMetadataRepository.save(metadata); - - students.add(savedStudent); - } - - return students; - } - - private List createLessons(List instructors) { - List lessons = new ArrayList<>(); - Category[] categories = Category.values(); - - for (User instructor : instructors) { - // 각 강사당 2-4개의 레슨 생성 - int lessonCount = random.nextInt(3) + 2; - - for (int i = 0; i < lessonCount; i++) { - Category category = categories[random.nextInt(categories.length)]; - - Lesson lesson = Lesson.builder() - .lessonLeader(instructor.getId()) - .lessonName(category.name() + " 레슨" + (i + 1)) - .description("최고의 " + category.name() + " 레슨입니다!") - .maxParticipants(random.nextInt(10) + 5) // 5-14명 - .startAt(LocalDateTime.now().plusDays(random.nextInt(30) + 1)) - .endAt(LocalDateTime.now().plusDays(random.nextInt(30) + 1).plusHours(2)) - .price(random.nextInt(50000) + 10000) // 10,000-60,000원 - .category(category) - .openTime(null) - .openRun(false) - .city("서울시") - .district("강남구") - .dong("역삼동") - .addressDetail("테스트 주소 " + random.nextInt(100)) - .build(); - - lessons.add(lessonRepository.save(lesson)); - } - } - - return lessons; - } - - private void createReviews(List instructors, List students, List lessons) { - for (User instructor : instructors) { - ProfileMetadata metadata = profileMetadataRepository.findByUserId(instructor.getId()) - .orElseThrow(); - - // 해당 강사의 레슨들 찾기 - List instructorLessons = lessons.stream() - .filter(lesson -> lesson.getLessonLeader().equals(instructor.getId())) - .toList(); - - if (instructorLessons.isEmpty()) { - continue; - } - - // reviewCount만큼 실제 리뷰 생성 - int reviewCount = metadata.getReviewCount(); - - for (int i = 0; i < reviewCount; i++) { - User randomStudent = students.get(random.nextInt(students.size())); - Lesson randomLesson = instructorLessons.get(random.nextInt(instructorLessons.size())); - - // 평점은 metadata의 rating 주변으로 생성 - double baseRating = metadata.getRating(); - double reviewRating = Math.max(1.0d, Math.min(5.0d, - baseRating + (random.nextDouble() - 0.5d) * 2)); // ±1점 범위 - - Review review = Review.builder() - .reviewer(randomStudent) - .reviewee(instructor) - .lesson(randomLesson) - .rating(reviewRating) - .content("좋은 레슨이었습니다! 추천해요.") - .image(random.nextBoolean() ? "https://example.com/review" + i + ".jpg" : null) - .build(); - - reviewRepository.save(review); - } - } - } - - private int generateReviewCount() { - // 랭킹에 들어갈 강사들(20개 이상)과 그렇지 않은 강사들 섞어서 생성 - int rand = random.nextInt(100); - if (rand < 60) { // 60% 확률로 랭킹 대상 - return random.nextInt(50) + 20; // 20-69개 - } else { // 40% 확률로 랭킹 비대상 - return random.nextInt(20); // 0-19개 - } - } - - private double generateRating() { - // 3.0 ~ 5.0 사이의 평점 생성 (소수점 1자리) - double rating = 3.0d + random.nextDouble() * 2.0d; - return Math.round(rating * 10) / 10.0d; - } -} +// package com.threestar.trainus.global.config; +// +// import java.time.LocalDateTime; +// import java.util.ArrayList; +// import java.util.List; +// import java.util.Random; +// +// import org.springframework.boot.CommandLineRunner; +// import org.springframework.security.crypto.password.PasswordEncoder; +// import org.springframework.stereotype.Component; +// import org.springframework.transaction.annotation.Transactional; +// +// import com.threestar.trainus.domain.lesson.admin.entity.Category; +// import com.threestar.trainus.domain.lesson.admin.entity.Lesson; +// import com.threestar.trainus.domain.lesson.admin.repository.LessonRepository; +// import com.threestar.trainus.domain.metadata.entity.ProfileMetadata; +// import com.threestar.trainus.domain.metadata.mapper.ProfileMetadataMapper; +// import com.threestar.trainus.domain.metadata.repository.ProfileMetadataRepository; +// import com.threestar.trainus.domain.profile.entity.Profile; +// import com.threestar.trainus.domain.profile.mapper.ProfileMapper; +// import com.threestar.trainus.domain.profile.repository.ProfileRepository; +// import com.threestar.trainus.domain.review.entity.Review; +// import com.threestar.trainus.domain.review.repository.ReviewRepository; +// import com.threestar.trainus.domain.user.entity.User; +// import com.threestar.trainus.domain.user.entity.UserRole; +// import com.threestar.trainus.domain.user.repository.UserRepository; +// +// import lombok.RequiredArgsConstructor; +// import lombok.extern.slf4j.Slf4j; +// +// @Slf4j +// @Component +// @org.springframework.context.annotation.Profile("dev") // dev 프로필에서만 실행 +// @RequiredArgsConstructor +// public class MockDataInitializer implements CommandLineRunner { +// +// private final UserRepository userRepository; +// private final ProfileRepository profileRepository; +// private final ProfileMetadataRepository profileMetadataRepository; +// private final LessonRepository lessonRepository; +// private final ReviewRepository reviewRepository; +// private final PasswordEncoder passwordEncoder; +// +// private final Random random = new Random(); +// +// @Override +// @Transactional +// public void run(String... args) throws Exception { +// if (userRepository.count() > 0) { +// log.info("데이터가 이미 존재합니다. Mock 데이터 생성을 건너뜁니다."); +// return; +// } +// +// log.info("Mock 데이터 생성 시작..."); +// +// // 1. 강사 유저 생성 (15명) +// List instructors = createInstructors(); +// +// // 2. 수강생 유저 생성 (50명) +// List students = createStudents(); +// +// // 3. 각 강사별로 레슨 생성 +// List lessons = createLessons(instructors); +// +// // 4. 리뷰 생성 (ProfileMetadata 업데이트) +// createReviews(instructors, students, lessons); +// +// log.info("Mock 데이터 생성 완료!"); +// log.info("생성된 데이터: 강사 {}명, 수강생 {}명, 레슨 {}개", +// instructors.size(), students.size(), lessons.size()); +// } +// +// private List createInstructors() { +// List instructors = new ArrayList<>(); +// String[] instructorNames = { +// "헬스왕김철수", "요가마스터", "필라테스여신", "러닝코치박", "수영선수이", +// "테니스프로", "복싱챔피언", "클라이밍킹", "골프레슨프로", "댄스퀸", +// "크로스핏코치", "배드민턴고수", "탁구선수", "농구코치", "축구감독" +// }; +// +// for (int i = 0; i < instructorNames.length; i++) { +// User instructor = User.builder() +// .email("instructor" + (i + 1) + "@test.com") +// .password(passwordEncoder.encode("password123")) +// .nickname(instructorNames[i]) +// .role(UserRole.USER) +// .build(); +// +// User savedInstructor = userRepository.save(instructor); +// +// // Profile 생성 +// Profile profile = ProfileMapper.toDefaultEntity(savedInstructor); +// profile.updateProfileImage( +// "https://example.com/instructor" + (i + 1) + ".jpg", +// instructorNames[i] + "입니다. 최고의 레슨을 제공합니다!" +// ); +// profileRepository.save(profile); +// +// // ProfileMetadata 생성 (랭킹용 데이터) +// ProfileMetadata metadata = ProfileMetadata.builder() +// .user(savedInstructor) +// .reviewCount(generateReviewCount()) +// .rating(generateRating()) +// .build(); +// profileMetadataRepository.save(metadata); +// +// instructors.add(savedInstructor); +// } +// +// return instructors; +// } +// +// private List createStudents() { +// List students = new ArrayList<>(); +// +// for (int i = 0; i < 50; i++) { +// User student = User.builder() +// .email("student" + (i + 1) + "@test.com") +// .password(passwordEncoder.encode("password123")) +// .nickname("수강생" + (i + 1)) +// .role(UserRole.USER) +// .build(); +// +// User savedStudent = userRepository.save(student); +// +// // Profile 생성 +// Profile profile = ProfileMapper.toDefaultEntity(savedStudent); +// profile.updateProfileImage( +// "https://example.com/student" + (i + 1) + ".jpg", +// "운동을 열심히 하는 수강생입니다!" +// ); +// profileRepository.save(profile); +// +// // 수강생은 메타데이터 기본값 +// ProfileMetadata metadata = ProfileMetadataMapper.toDefaultEntity(savedStudent); +// profileMetadataRepository.save(metadata); +// +// students.add(savedStudent); +// } +// +// return students; +// } +// +// private List createLessons(List instructors) { +// List lessons = new ArrayList<>(); +// Category[] categories = Category.values(); +// +// for (User instructor : instructors) { +// // 각 강사당 2-4개의 레슨 생성 +// int lessonCount = random.nextInt(3) + 2; +// +// for (int i = 0; i < lessonCount; i++) { +// Category category = categories[random.nextInt(categories.length)]; +// +// Lesson lesson = Lesson.builder() +// .lessonLeader(instructor.getId()) +// .lessonName(category.name() + " 레슨" + (i + 1)) +// .description("최고의 " + category.name() + " 레슨입니다!") +// .maxParticipants(random.nextInt(10) + 5) // 5-14명 +// .startAt(LocalDateTime.now().plusDays(random.nextInt(30) + 1)) +// .endAt(LocalDateTime.now().plusDays(random.nextInt(30) + 1).plusHours(2)) +// .price(random.nextInt(50000) + 10000) // 10,000-60,000원 +// .category(category) +// .openTime(null) +// .openRun(false) +// .city("서울시") +// .district("강남구") +// .dong("역삼동") +// .addressDetail("테스트 주소 " + random.nextInt(100)) +// .build(); +// +// lessons.add(lessonRepository.save(lesson)); +// } +// } +// +// return lessons; +// } +// +// private void createReviews(List instructors, List students, List lessons) { +// for (User instructor : instructors) { +// ProfileMetadata metadata = profileMetadataRepository.findByUserId(instructor.getId()) +// .orElseThrow(); +// +// // 해당 강사의 레슨들 찾기 +// List instructorLessons = lessons.stream() +// .filter(lesson -> lesson.getLessonLeader().equals(instructor.getId())) +// .toList(); +// +// if (instructorLessons.isEmpty()) { +// continue; +// } +// +// // reviewCount만큼 실제 리뷰 생성 +// int reviewCount = metadata.getReviewCount(); +// +// for (int i = 0; i < reviewCount; i++) { +// User randomStudent = students.get(random.nextInt(students.size())); +// Lesson randomLesson = instructorLessons.get(random.nextInt(instructorLessons.size())); +// +// // 평점은 metadata의 rating 주변으로 생성 +// double baseRating = metadata.getRating(); +// double reviewRating = Math.max(1.0d, Math.min(5.0d, +// baseRating + (random.nextDouble() - 0.5d) * 2)); // ±1점 범위 +// +// Review review = Review.builder() +// .reviewer(randomStudent) +// .reviewee(instructor) +// .lesson(randomLesson) +// .rating(reviewRating) +// .content("좋은 레슨이었습니다! 추천해요.") +// .image(random.nextBoolean() ? "https://example.com/review" + i + ".jpg" : null) +// .build(); +// +// reviewRepository.save(review); +// } +// } +// } +// +// private int generateReviewCount() { +// // 랭킹에 들어갈 강사들(20개 이상)과 그렇지 않은 강사들 섞어서 생성 +// int rand = random.nextInt(100); +// if (rand < 60) { // 60% 확률로 랭킹 대상 +// return random.nextInt(50) + 20; // 20-69개 +// } else { // 40% 확률로 랭킹 비대상 +// return random.nextInt(20); // 0-19개 +// } +// } +// +// private double generateRating() { +// // 3.0 ~ 5.0 사이의 평점 생성 (소수점 1자리) +// double rating = 3.0d + random.nextDouble() * 2.0d; +// return Math.round(rating * 10) / 10.0d; +// } +// } From b9c26ad79bc2287d8dd06e51c6e81e4e1b449ae3 Mon Sep 17 00:00:00 2001 From: limchangin Date: Wed, 16 Jul 2025 16:40:51 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98=EC=A0=95,=20=EC=9E=90?= =?UTF-8?q?=EA=B8=B0=EC=86=8C=EA=B0=9C=20=EC=88=98=EC=A0=95=20api=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/profile/dto/ImageUpdateResponseDto.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/com/threestar/trainus/domain/profile/dto/ImageUpdateResponseDto.java diff --git a/src/main/java/com/threestar/trainus/domain/profile/dto/ImageUpdateResponseDto.java b/src/main/java/com/threestar/trainus/domain/profile/dto/ImageUpdateResponseDto.java new file mode 100644 index 0000000..d2c4fa6 --- /dev/null +++ b/src/main/java/com/threestar/trainus/domain/profile/dto/ImageUpdateResponseDto.java @@ -0,0 +1,8 @@ +package com.threestar.trainus.domain.profile.dto; + +public record ImageUpdateResponseDto( + Long userId, + String nickname, + String profileImage +) { +} From b74698d55cafc9fd1bfdcd97c86fdd2121f643c2 Mon Sep 17 00:00:00 2001 From: limchangin Date: Thu, 17 Jul 2025 10:43:03 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94=20i?= =?UTF-8?q?mport=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/lesson/admin/dto/LessonApplicationResponseDto.java | 1 - .../trainus/domain/lesson/admin/dto/ParticipantDto.java | 1 - .../domain/lesson/admin/mapper/LessonApplicationMapper.java | 1 - .../domain/lesson/admin/mapper/LessonParticipantMapper.java | 1 - 4 files changed, 4 deletions(-) diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java index f8832a8..0929982 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/LessonApplicationResponseDto.java @@ -3,7 +3,6 @@ import java.time.LocalDateTime; import com.threestar.trainus.domain.lesson.admin.entity.ApplicationStatus; -import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; import lombok.Builder; diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java index 8824894..21f3062 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/dto/ParticipantDto.java @@ -2,7 +2,6 @@ import java.time.LocalDateTime; -import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; import lombok.Builder; diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java index 7b073be..8ca5746 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonApplicationMapper.java @@ -5,7 +5,6 @@ import com.threestar.trainus.domain.lesson.admin.dto.LessonApplicationListResponseDto; import com.threestar.trainus.domain.lesson.admin.dto.LessonApplicationResponseDto; import com.threestar.trainus.domain.lesson.admin.entity.LessonApplication; -import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; public class LessonApplicationMapper { diff --git a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java index d1e7a57..075208b 100644 --- a/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java +++ b/src/main/java/com/threestar/trainus/domain/lesson/admin/mapper/LessonParticipantMapper.java @@ -5,7 +5,6 @@ import com.threestar.trainus.domain.lesson.admin.dto.ParticipantDto; import com.threestar.trainus.domain.lesson.admin.dto.ParticipantListResponseDto; import com.threestar.trainus.domain.lesson.admin.entity.LessonApplication; -import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ProfileResponseDto; public class LessonParticipantMapper { From 72fe62e7ad27c9bd8ec0ce0d8b7da57985273fe0 Mon Sep 17 00:00:00 2001 From: limchangin Date: Thu, 17 Jul 2025 11:08:58 +0900 Subject: [PATCH 5/5] refactor: checkStyle --- .../domain/profile/controller/ProfileController.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java b/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java index 187f565..4cd0738 100644 --- a/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java +++ b/src/main/java/com/threestar/trainus/domain/profile/controller/ProfileController.java @@ -9,18 +9,17 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.threestar.trainus.domain.profile.dto.IntroUpdateRequestDto; -import com.threestar.trainus.domain.profile.dto.ProfileDetailResponseDto; -import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; import com.threestar.trainus.domain.profile.dto.ImageUpdateRequestDto; +import com.threestar.trainus.domain.profile.dto.ImageUpdateResponseDto; +import com.threestar.trainus.domain.profile.dto.IntroUpdateRequestDto; import com.threestar.trainus.domain.profile.dto.IntroUpdateResponseDto; +import com.threestar.trainus.domain.profile.dto.ProfileDetailResponseDto; import com.threestar.trainus.domain.profile.service.ProfileFacadeService; import com.threestar.trainus.global.annotation.LoginUser; import com.threestar.trainus.global.unit.BaseResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; - import jakarta.validation.Valid; import lombok.RequiredArgsConstructor;