From 431c8337b61dab64d0c81b60ee3123ee748e8ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EC=A7=80=EC=84=AD?= <41179427+tlswltjq@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:46:59 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=EC=8B=9C=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat : 파일 업로드시 원본 파일명을 받도록 수정 * Feat : 파일명으로부터 확장자를 체크하고 획득하며 URL생성시 사용하도록 수정 --- .../video/controller/VideoController.java | 4 +-- .../file/video/service/FileManager.java | 33 +++++++++++++++++-- .../domain/file/video/service/S3Service.java | 7 ++-- .../domain/file/service/S3ServiceTest.java | 4 +-- 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/back/src/main/java/com/back/domain/file/video/controller/VideoController.java b/back/src/main/java/com/back/domain/file/video/controller/VideoController.java index 4539e52d..f682c79c 100644 --- a/back/src/main/java/com/back/domain/file/video/controller/VideoController.java +++ b/back/src/main/java/com/back/domain/file/video/controller/VideoController.java @@ -19,8 +19,8 @@ public class VideoController { @GetMapping("/videos/upload") @PreAuthorize("hasRole('ADMIN')") @Operation(summary = "업로드용 URL 요청", description = "파일 업로드를 위한 Presigned URL을 발급받습니다.") - public RsData getUploadUrl() { - PresignedUrlResponse uploadUrl = fileManager.getUploadUrl(); + public RsData getUploadUrl(@RequestParam String filename) { + PresignedUrlResponse uploadUrl = fileManager.getUploadUrl(filename); UploadUrlGetResponse response = new UploadUrlGetResponse(uploadUrl.url().toString(), uploadUrl.expiresAt()); return new RsData<>("200", "업로드용 URL 요청완료", response); } diff --git a/back/src/main/java/com/back/domain/file/video/service/FileManager.java b/back/src/main/java/com/back/domain/file/video/service/FileManager.java index cbdc31ca..50010938 100644 --- a/back/src/main/java/com/back/domain/file/video/service/FileManager.java +++ b/back/src/main/java/com/back/domain/file/video/service/FileManager.java @@ -2,6 +2,7 @@ import com.back.domain.file.video.dto.service.PresignedUrlResponse; +import com.back.global.exception.ServiceException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -15,14 +16,42 @@ public class FileManager { private final VideoService videoService; private final S3Service s3Service; - public PresignedUrlResponse getUploadUrl() { + public PresignedUrlResponse getUploadUrl(String filename) { + String contentType = validateAndGetContentType(filename); + String ext = extractExt(filename); String uuid = UUID.randomUUID().toString(); + String objectKey = "videos/" + uuid + "." + ext; Integer expires = 5; - URL url = s3Service.generateUploadUrl("videos/"+uuid, expires); + URL url = s3Service.generateUploadUrl(objectKey, expires, contentType); LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(expires); return new PresignedUrlResponse(url, expiresAt); } + private String extractExt(String filename) { + int pos = filename.lastIndexOf("."); + return filename.substring(pos + 1); + } + + private String validateAndGetContentType(String filename) { + String ext = extractExt(filename).toLowerCase(); + switch (ext) { + case "mp4": + return "video/mp4"; + case "mov": + return "video/quicktime"; + case "avi": + return "video/x-msvideo"; + case "wmv": + return "video/x-ms-wmv"; + case "mkv": + return "video/x-matroska"; + case "webm": + return "video/webm"; + default: + throw new ServiceException("400", "지원하지 않는 동영상 파일 형식입니다: " + ext); + } + } + public PresignedUrlResponse getDownloadUrl(String objectKey) { Integer expires = 60; URL url = s3Service.generateDownloadUrl(objectKey, expires); diff --git a/back/src/main/java/com/back/domain/file/video/service/S3Service.java b/back/src/main/java/com/back/domain/file/video/service/S3Service.java index 43b4c1bb..d2aef396 100644 --- a/back/src/main/java/com/back/domain/file/video/service/S3Service.java +++ b/back/src/main/java/com/back/domain/file/video/service/S3Service.java @@ -21,12 +21,13 @@ public class S3Service { @Value("${aws.s3.bucket}") private String bucket; - public URL generateUploadUrl(String objectKey, Integer expireMinutes) { + public URL generateUploadUrl(String objectKey, Integer expireMinutes, String contentType) { validateRequest(objectKey); PutObjectRequest request = PutObjectRequest.builder() .bucket(bucket) .key(objectKey) + .contentType(contentType) .build(); PresignedPutObjectRequest presignedRequest = @@ -41,8 +42,8 @@ public URL generateUploadUrl(String objectKey, Integer expireMinutes) { return presignedRequest.url(); } - public URL generateUploadUrl(String objectKey) { - return generateUploadUrl(objectKey, 30); + public URL generateUploadUrl(String objectKey, String contentType) { + return generateUploadUrl(objectKey, 30, contentType); } public URL generateDownloadUrl(String objectKey, Integer expireMinutes) { diff --git a/back/src/test/java/com/back/domain/file/service/S3ServiceTest.java b/back/src/test/java/com/back/domain/file/service/S3ServiceTest.java index 47430cd5..acd66d2a 100644 --- a/back/src/test/java/com/back/domain/file/service/S3ServiceTest.java +++ b/back/src/test/java/com/back/domain/file/service/S3ServiceTest.java @@ -46,7 +46,7 @@ void generateUploadUrlTest() throws MalformedURLException { when(mocked.url()).thenReturn(new URL("http://localhost:8080/upload")); - URL url = s3Service.generateUploadUrl(objectKey); + URL url = s3Service.generateUploadUrl(objectKey, "video/mp4"); assertThat(url).isNotNull(); assertThat(url.toString()).isEqualTo("http://localhost:8080/upload"); @@ -77,7 +77,7 @@ void generateUploadUrl_PresignRequestNull_Test() { when(presigner.presignPutObject(any(Consumer.class))).thenReturn(null); try { - s3Service.generateUploadUrl(objectKey); + s3Service.generateUploadUrl(objectKey, "video/mp4"); } catch (Exception e) { assertThat(e).isInstanceOf(ServiceException.class); } From 42eee385139c81d35eb4da34f5e17beb7d231127 Mon Sep 17 00:00:00 2001 From: dooongdaeng Date: Mon, 13 Oct 2025 14:31:31 +0900 Subject: [PATCH 2/3] Feat/70 1 (#234) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat:ã…:job 연결, admin dto 수정 * fix:test 오류 해결 --------- Co-authored-by: sso0om Co-authored-by: 석희성 Co-authored-by: 신지섭 <41179427+tlswltjq@users.noreply.github.com> Co-authored-by: dbfgml20 <53211374+dbfgml2000@users.noreply.github.com> --- .../controller/MemberAuthController.java | 7 +- .../member/member/dto/MemberMeResponse.java | 27 ++++++ .../member/dto/MemberSearchResponse.java | 10 ++- .../member/dto/MenteeMyPageResponse.java | 4 +- .../member/dto/MentorMyPageResponse.java | 4 +- .../member/member/service/MemberService.java | 83 ++++++++++++++--- .../domain/member/mentee/entity/Mentee.java | 14 ++- .../domain/member/mentor/entity/Mentor.java | 14 ++- .../repository/MentorRoadmapRepository.java | 2 +- .../roadmap/service/MentorRoadmapService.java | 6 +- .../back/global/initData/RoadmapInitData.java | 90 ++++++++++--------- .../controller/AdminMemberControllerTest.java | 20 +++-- .../controller/MemberAuthControllerTest.java | 51 ++++++++++- .../MemberMyPageControllerTest.java | 31 ++++++- .../java/com/back/fixture/JobFixture.java | 56 ++++++++++++ .../com/back/fixture/MemberTestFixture.java | 29 ++++-- .../java/com/back/fixture/MenteeFixture.java | 11 ++- .../java/com/back/fixture/MentorFixture.java | 10 +-- 18 files changed, 364 insertions(+), 105 deletions(-) create mode 100644 back/src/main/java/com/back/domain/member/member/dto/MemberMeResponse.java create mode 100644 back/src/test/java/com/back/fixture/JobFixture.java diff --git a/back/src/main/java/com/back/domain/member/member/controller/MemberAuthController.java b/back/src/main/java/com/back/domain/member/member/controller/MemberAuthController.java index 5827ee62..49c156f5 100644 --- a/back/src/main/java/com/back/domain/member/member/controller/MemberAuthController.java +++ b/back/src/main/java/com/back/domain/member/member/controller/MemberAuthController.java @@ -4,6 +4,7 @@ import com.back.domain.member.member.dto.MentorSignupVerifyRequest; import com.back.domain.member.member.dto.MentorVerificationRequest; import com.back.domain.member.member.dto.LoginRequest; +import com.back.domain.member.member.dto.MemberMeResponse; import com.back.domain.member.member.entity.Member; import com.back.domain.member.member.service.MemberService; import com.back.global.rq.Rq; @@ -87,9 +88,9 @@ public RsData logout() { @GetMapping("/me") @Operation(summary = "사용자 정보 조회") - public RsData me() { - Member actor = memberService.getCurrentUser(rq.getActor()); - return new RsData<>("200-5", "사용자 정보 조회 성공", actor); + public RsData me() { + MemberMeResponse response = memberService.getMemberMe(rq.getActor()); + return new RsData<>("200-5", "사용자 정보 조회 성공", response); } @PostMapping("/refresh") diff --git a/back/src/main/java/com/back/domain/member/member/dto/MemberMeResponse.java b/back/src/main/java/com/back/domain/member/member/dto/MemberMeResponse.java new file mode 100644 index 00000000..d66a0bca --- /dev/null +++ b/back/src/main/java/com/back/domain/member/member/dto/MemberMeResponse.java @@ -0,0 +1,27 @@ +package com.back.domain.member.member.dto; + +import com.back.domain.member.member.entity.Member; + +public record MemberMeResponse( + Long memberId, + String publicId, + String email, + String name, + String nickname, + String role, + Long mentorId, + Long menteeId +) { + public static MemberMeResponse of(Member member, Long mentorId, Long menteeId) { + return new MemberMeResponse( + member.getId(), + member.getPublicId(), + member.getEmail(), + member.getName(), + member.getNickname(), + member.getRole().name(), + mentorId, + menteeId + ); + } +} diff --git a/back/src/main/java/com/back/domain/member/member/dto/MemberSearchResponse.java b/back/src/main/java/com/back/domain/member/member/dto/MemberSearchResponse.java index 0d64aa4f..4da2b5b2 100644 --- a/back/src/main/java/com/back/domain/member/member/dto/MemberSearchResponse.java +++ b/back/src/main/java/com/back/domain/member/member/dto/MemberSearchResponse.java @@ -15,9 +15,10 @@ public record MemberSearchResponse( Boolean isDeleted, LocalDateTime createdAt, LocalDateTime modifiedAt, - String career, // 멘토인 경우에만 값이 있음 (TODO: Job 연결 후 수정 예정) + String job, // 멘토/멘티 모두: 직업명 또는 관심분야 Integer careerYears, // 멘토인 경우에만 값이 있음 - String interestedField // 멘티인 경우에만 값이 있음 (TODO: Job 연결 후 수정 예정) + Long mentorId, // 멘토 ID (멘토인 경우에만 값이 있음) + Long menteeId // 멘티 ID (멘티인 경우에만 값이 있음) ) { public static MemberSearchResponse from(Member member, Mentor mentor, Mentee mentee) { return new MemberSearchResponse( @@ -29,9 +30,10 @@ public static MemberSearchResponse from(Member member, Mentor mentor, Mentee men member.getIsDeleted(), member.getCreateDate(), member.getModifyDate(), - mentor != null ? "TODO: Job 연결 필요" : null, // TODO: Job 연결 후 수정 + mentor != null ? mentor.getJob().getName() : (mentee != null ? mentee.getJob().getName() : null), mentor != null ? mentor.getCareerYears() : null, - mentee != null ? "TODO: Job 연결 필요" : null // TODO: Job 연결 후 수정 + mentor != null ? mentor.getId() : null, + mentee != null ? mentee.getId() : null ); } } \ No newline at end of file diff --git a/back/src/main/java/com/back/domain/member/member/dto/MenteeMyPageResponse.java b/back/src/main/java/com/back/domain/member/member/dto/MenteeMyPageResponse.java index 68410f91..414d6ccc 100644 --- a/back/src/main/java/com/back/domain/member/member/dto/MenteeMyPageResponse.java +++ b/back/src/main/java/com/back/domain/member/member/dto/MenteeMyPageResponse.java @@ -8,7 +8,7 @@ public record MenteeMyPageResponse( String email, String name, String nickname, - Long jobId + String job ) { public static MenteeMyPageResponse from(Member member, Mentee mentee) { return new MenteeMyPageResponse( @@ -16,7 +16,7 @@ public static MenteeMyPageResponse from(Member member, Mentee mentee) { member.getEmail(), member.getName(), member.getNickname(), - mentee.getJobId() + mentee.getJob().getName() ); } } \ No newline at end of file diff --git a/back/src/main/java/com/back/domain/member/member/dto/MentorMyPageResponse.java b/back/src/main/java/com/back/domain/member/member/dto/MentorMyPageResponse.java index 18e24e37..11ae0933 100644 --- a/back/src/main/java/com/back/domain/member/member/dto/MentorMyPageResponse.java +++ b/back/src/main/java/com/back/domain/member/member/dto/MentorMyPageResponse.java @@ -8,7 +8,7 @@ public record MentorMyPageResponse( String email, String name, String nickname, - Long jobId, + String job, Double rate, Integer careerYears ) { @@ -18,7 +18,7 @@ public static MentorMyPageResponse from(Member member, Mentor mentor) { member.getEmail(), member.getName(), member.getNickname(), - mentor.getJobId(), + mentor.getJob().getName(), mentor.getRate(), mentor.getCareerYears() ); diff --git a/back/src/main/java/com/back/domain/member/member/service/MemberService.java b/back/src/main/java/com/back/domain/member/member/service/MemberService.java index d25d195c..32b57a50 100644 --- a/back/src/main/java/com/back/domain/member/member/service/MemberService.java +++ b/back/src/main/java/com/back/domain/member/member/service/MemberService.java @@ -1,5 +1,7 @@ package com.back.domain.member.member.service; +import com.back.domain.job.job.entity.Job; +import com.back.domain.job.job.repository.JobRepository; import com.back.domain.member.member.dto.*; import com.back.domain.member.member.entity.Member; import com.back.domain.member.member.repository.MemberRepository; @@ -24,6 +26,7 @@ public class MemberService { private final MentorRepository mentorRepository; private final MenteeRepository menteeRepository; private final PasswordEncoder passwordEncoder; + private final JobRepository jobRepository; @Transactional public Member joinMentee(String email, String name, String nickname, String password, String interestedField) { @@ -44,8 +47,11 @@ public Member joinMentee(String email, String name, String nickname, String pass Member member = new Member(email, passwordEncoder.encode(password), name, nickname, Member.Role.MENTEE); Member savedMember = memberRepository.save(member); - // TODO: interestedField를 jobId로 매핑하는 로직 필요 - Mentee mentee = new Mentee(savedMember, null); + // interestedField로 Job 찾기 또는 생성 + Job job = jobRepository.findByName(interestedField) + .orElseGet(() -> jobRepository.save(new Job(interestedField, null))); + + Mentee mentee = new Mentee(savedMember, job); menteeRepository.save(mentee); return savedMember; @@ -70,8 +76,11 @@ public Member joinMentor(String email, String name, String nickname, String pass Member member = new Member(email, passwordEncoder.encode(password), name, nickname, Member.Role.MENTOR); Member savedMember = memberRepository.save(member); - // TODO: career를 jobId로 매핑하는 로직 필요 - Mentor mentor = new Mentor(savedMember, null, null, careerYears); + // career로 Job 찾기 또는 생성 + Job job = jobRepository.findByName(career) + .orElseGet(() -> jobRepository.save(new Job(career, null))); + + Mentor mentor = new Mentor(savedMember, job, null, careerYears); mentorRepository.save(mentor); return savedMember; @@ -146,6 +155,27 @@ public Member getCurrentUser(Member actor) { return actor; } + public MemberMeResponse getMemberMe(Member actor) { + if (actor == null) { + throw new ServiceException("401-1", "로그인이 필요합니다."); + } + + Long mentorId = null; + Long menteeId = null; + + if (actor.getRole() == Member.Role.MENTOR) { + mentorId = mentorRepository.findByMemberId(actor.getId()) + .map(Mentor::getId) + .orElse(null); + } else if (actor.getRole() == Member.Role.MENTEE) { + menteeId = menteeRepository.findByMemberId(actor.getId()) + .map(Mentee::getId) + .orElse(null); + } + + return MemberMeResponse.of(actor, mentorId, menteeId); + } + public Member refreshAccessToken(String refreshToken) { if (refreshToken.isBlank()) { throw new ServiceException("401-1", "Refresh token이 없습니다."); @@ -191,7 +221,17 @@ public void updateMentee(Member currentUser, MenteeUpdateRequest request) { member.updateNickname(request.nickname()); memberRepository.save(member); - // TODO: interestedField를 jobId로 매핑하는 로직 필요 (현재는 기존 jobId 유지) + // Mentee 정보 업데이트 (interestedField) + if (request.interestedField() != null) { + Mentee mentee = menteeRepository.findByMemberId(currentUser.getId()) + .orElseThrow(() -> new ServiceException("404-2", "멘티 정보를 찾을 수 없습니다.")); + + Job job = jobRepository.findByName(request.interestedField()) + .orElseGet(() -> jobRepository.save(new Job(request.interestedField(), null))); + + mentee.updateJob(job); + menteeRepository.save(mentee); + } } public MentorMyPageResponse getMentorMyPage(Member currentUser) { @@ -216,11 +256,18 @@ public void updateMentor(Member currentUser, MentorUpdateRequest request) { member.updateNickname(request.nickname()); memberRepository.save(member); - // Mentor 정보 업데이트 (경력연수) - mentor.updateCareerYears(request.careerYears()); - mentorRepository.save(mentor); + // Mentor 정보 업데이트 (경력연수, career) + if (request.careerYears() != null) { + mentor.updateCareerYears(request.careerYears()); + } - // TODO: career를 jobId로 매핑하는 로직 필요 (현재는 기존 jobId 유지) + if (request.career() != null) { + Job job = jobRepository.findByName(request.career()) + .orElseGet(() -> jobRepository.save(new Job(request.career(), null))); + mentor.updateJob(job); + } + + mentorRepository.save(mentor); } @@ -288,11 +335,25 @@ public void updateMemberByAdmin(Long memberId, String name, String nickname, Str Mentor mentor = mentorRepository.findByMemberIdIncludingDeleted(member.getId()) .orElse(null); if (mentor != null) { - if (careerYears != null) mentor.updateCareerYears(careerYears); + if (careerYears != null) { + mentor.updateCareerYears(careerYears); + } + if (career != null) { + Job job = jobRepository.findByName(career) + .orElseGet(() -> jobRepository.save(new Job(career, null))); + mentor.updateJob(job); + } mentorRepository.save(mentor); } } else if (member.getRole() == Member.Role.MENTEE && interestedField != null) { - // TODO: interestedField 업데이트 로직 필요 (Mentee 엔티티에 업데이트 메서드가 있을 때) + Mentee mentee = menteeRepository.findByMemberIdIncludingDeleted(member.getId()) + .orElse(null); + if (mentee != null) { + Job job = jobRepository.findByName(interestedField) + .orElseGet(() -> jobRepository.save(new Job(interestedField, null))); + mentee.updateJob(job); + menteeRepository.save(mentee); + } } } diff --git a/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java b/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java index c2a87c85..18adcbef 100644 --- a/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java +++ b/back/src/main/java/com/back/domain/member/mentee/entity/Mentee.java @@ -1,5 +1,6 @@ package com.back.domain.member.mentee.entity; +import com.back.domain.job.job.entity.Job; import com.back.domain.member.member.entity.Member; import com.back.global.jpa.BaseEntity; import jakarta.persistence.*; @@ -15,19 +16,24 @@ public class Mentee extends BaseEntity { @JoinColumn(name = "member_id", nullable = false) private Member member; - @Column(name = "job_id") - private Long jobId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "job_id", nullable = false) + private Job job; @Column(nullable = false) private Boolean isDeleted = false; @Builder - public Mentee(Member member, Long jobId) { + public Mentee(Member member, Job job) { this.member = member; - this.jobId = jobId; + this.job = job; this.isDeleted = false; } + public void updateJob(Job job) { + this.job = job; + } + public void delete() { this.isDeleted = true; } diff --git a/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java b/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java index 1cbb52bc..37f2dda4 100644 --- a/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java +++ b/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java @@ -1,5 +1,6 @@ package com.back.domain.member.mentor.entity; +import com.back.domain.job.job.entity.Job; import com.back.domain.member.member.entity.Member; import com.back.global.jpa.BaseEntity; import jakarta.persistence.*; @@ -15,8 +16,9 @@ public class Mentor extends BaseEntity { @JoinColumn(name = "member_id", nullable = false) private Member member; - @Column(name = "job_id") - private Long jobId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "job_id", nullable = false) + private Job job; @Column private Double rate; @@ -28,14 +30,18 @@ public class Mentor extends BaseEntity { private Boolean isDeleted = false; @Builder - public Mentor(Member member, Long jobId, Double rate, Integer careerYears) { + public Mentor(Member member, Job job, Double rate, Integer careerYears) { this.member = member; - this.jobId = jobId; + this.job = job; this.rate = rate; this.careerYears = careerYears; this.isDeleted = false; } + public void updateJob(Job job) { + this.job = job; + } + public void updateCareerYears(Integer careerYears) { this.careerYears = careerYears; } diff --git a/back/src/main/java/com/back/domain/roadmap/roadmap/repository/MentorRoadmapRepository.java b/back/src/main/java/com/back/domain/roadmap/roadmap/repository/MentorRoadmapRepository.java index b9226b1a..0de5e711 100644 --- a/back/src/main/java/com/back/domain/roadmap/roadmap/repository/MentorRoadmapRepository.java +++ b/back/src/main/java/com/back/domain/roadmap/roadmap/repository/MentorRoadmapRepository.java @@ -50,7 +50,7 @@ public interface MentorRoadmapRepository extends JpaRepository findAllByMentorJobIdWithNodes(@Param("jobId") Long jobId); diff --git a/back/src/main/java/com/back/domain/roadmap/roadmap/service/MentorRoadmapService.java b/back/src/main/java/com/back/domain/roadmap/roadmap/service/MentorRoadmapService.java index 3a0c5e22..6ef9c698 100644 --- a/back/src/main/java/com/back/domain/roadmap/roadmap/service/MentorRoadmapService.java +++ b/back/src/main/java/com/back/domain/roadmap/roadmap/service/MentorRoadmapService.java @@ -61,7 +61,7 @@ public MentorRoadmapSaveResponse create(Long mentorId, MentorRoadmapSaveRequest // CASCADE로 노드들이 자동 저장됨 (추가 save() 호출 불필요) mentorRoadmap.addNodes(allNodes); - eventPublisher.publishEvent(new MentorRoadmapChangeEvent(mentor.getJobId())); + eventPublisher.publishEvent(new MentorRoadmapChangeEvent(mentor.getJob().getId())); log.info("멘토 로드맵 생성 완료 - 멘토 ID: {}, 로드맵 ID: {}, 노드 수: {} (cascade 활용)", mentorId, mentorRoadmap.getId(), mentorRoadmap.getNodes().size()); @@ -134,7 +134,7 @@ public MentorRoadmapSaveResponse update(Long id, Long mentorId, MentorRoadmapSav log.info("멘토 로드맵 수정 완료 - 로드맵 ID: {}, 노드 수: {} (cascade 활용)", mentorRoadmap.getId(), mentorRoadmap.getNodes().size()); - eventPublisher.publishEvent(new MentorRoadmapChangeEvent(mentorRoadmap.getMentor().getJobId())); + eventPublisher.publishEvent(new MentorRoadmapChangeEvent(mentorRoadmap.getMentor().getJob().getId())); return new MentorRoadmapSaveResponse( mentorRoadmap.getId(), @@ -157,7 +157,7 @@ public void delete(Long roadmapId, Long mentorId) { throw new ServiceException("403", "본인의 로드맵만 삭제할 수 있습니다."); } - Long jobId = mentorRoadmap.getMentor().getJobId(); + Long jobId = mentorRoadmap.getMentor().getJob().getId(); // 1. 관련 노드들을 먼저 직접 삭제 roadmapNodeRepository.deleteByRoadmapIdAndRoadmapType( diff --git a/back/src/main/java/com/back/global/initData/RoadmapInitData.java b/back/src/main/java/com/back/global/initData/RoadmapInitData.java index 0a86e2a5..3e9568ba 100644 --- a/back/src/main/java/com/back/global/initData/RoadmapInitData.java +++ b/back/src/main/java/com/back/global/initData/RoadmapInitData.java @@ -70,10 +70,10 @@ public void runInitData() { // --- Job 초기화 --- public void initJobData() { - if (jobService.count() > 0) return; // 백엔드 개발자 - Job job1 = jobService.create("백엔드 개발자", "서버 사이드 로직을 구현하고, 데이터베이스 및 API를 설계·운영하는 개발자입니다."); + Job job1 = jobRepository.findByName("백엔드 개발자") + .orElseGet(() -> jobService.create("백엔드 개발자", "서버 사이드 로직을 구현하고, 데이터베이스 및 API를 설계·운영하는 개발자입니다.")); jobService.createAlias(job1, "백엔드"); jobService.createAlias(job1, "BE 개발자"); jobService.createAlias(job1, "Backend 개발자"); @@ -81,7 +81,8 @@ public void initJobData() { jobService.createAlias(job1, "API 개발자"); // 프론트엔드 개발자 - Job job2 = jobService.create("프론트엔드 개발자", "웹 또는 앱의 사용자 인터페이스(UI)와 사용자 경험(UX)을 담당하며, 사용자가 직접 보는 화면을 구현하는 개발자입니다."); + Job job2 = jobRepository.findByName("프론트엔드 개발자") + .orElseGet(() -> jobService.create("프론트엔드 개발자", "웹 또는 앱의 사용자 인터페이스(UI)와 사용자 경험(UX)을 담당하며, 사용자가 직접 보는 화면을 구현하는 개발자입니다.")); jobService.createAlias(job2, "프론트엔드"); jobService.createAlias(job2, "FE 개발자"); jobService.createAlias(job2, "Frontend 개발자"); @@ -90,58 +91,67 @@ public void initJobData() { jobService.createAlias(job2, "클라이언트 개발자"); // 모바일 앱 개발자 - Job job3 = jobService.create( - "모바일 앱 개발자", - "스마트폰과 태블릿 환경에서 동작하는 iOS 또는 Android 애플리케이션을 개발하는 직군으로, 플랫폼별 네이티브 또는 크로스플랫폼 기술을 활용합니다." - ); + Job job3 = jobRepository.findByName("모바일 앱 개발자") + .orElseGet(() -> jobService.create( + "모바일 앱 개발자", + "스마트폰과 태블릿 환경에서 동작하는 iOS 또는 Android 애플리케이션을 개발하는 직군으로, 플랫폼별 네이티브 또는 크로스플랫폼 기술을 활용합니다." + )); // 데이터 엔지니어 - Job job4 = jobService.create( - "데이터 엔지니어", - "데이터를 수집·저장·처리할 수 있는 파이프라인을 설계하고 구축하는 전문가로, 데이터 분석과 AI 모델링의 기반을 마련합니다." - ); + Job job4 = jobRepository.findByName("데이터 엔지니어") + .orElseGet(() -> jobService.create( + "데이터 엔지니어", + "데이터를 수집·저장·처리할 수 있는 파이프라인을 설계하고 구축하는 전문가로, 데이터 분석과 AI 모델링의 기반을 마련합니다." + )); // 데이터 분석가 - Job job5 = jobService.create( - "데이터 분석가", - "데이터를 기반으로 비즈니스 인사이트를 도출하고, 통계 분석과 시각화를 통해 의사결정을 지원하는 직군입니다." - ); + Job job5 = jobRepository.findByName("데이터 분석가") + .orElseGet(() -> jobService.create( + "데이터 분석가", + "데이터를 기반으로 비즈니스 인사이트를 도출하고, 통계 분석과 시각화를 통해 의사결정을 지원하는 직군입니다." + )); // AI / 머신러닝 엔지니어 - Job job6 = jobService.create( - "AI/ML 엔지니어", - "머신러닝과 딥러닝 알고리즘을 활용해 예측 모델과 인공지능 서비스를 개발하는 직군으로, 데이터 처리와 모델 학습에 대한 이해가 필요합니다." - ); + Job job6 = jobRepository.findByName("AI/ML 엔지니어") + .orElseGet(() -> jobService.create( + "AI/ML 엔지니어", + "머신러닝과 딥러닝 알고리즘을 활용해 예측 모델과 인공지능 서비스를 개발하는 직군으로, 데이터 처리와 모델 학습에 대한 이해가 필요합니다." + )); // DevOps 엔지니어 - Job job7 = jobService.create( - "DevOps 엔지니어", - "개발(Development)과 운영(Operations)을 연결하여 CI/CD 파이프라인, 인프라 자동화, 배포 환경을 최적화하는 엔지니어입니다." - ); + Job job7 = jobRepository.findByName("DevOps 엔지니어") + .orElseGet(() -> jobService.create( + "DevOps 엔지니어", + "개발(Development)과 운영(Operations)을 연결하여 CI/CD 파이프라인, 인프라 자동화, 배포 환경을 최적화하는 엔지니어입니다." + )); // 클라우드 엔지니어 - Job job8 = jobService.create( - "클라우드 엔지니어", - "AWS, GCP, Azure 등 클라우드 환경에서 인프라를 설계·배포·운영하며, 서비스의 안정성과 확장성을 책임지는 직군입니다." - ); + Job job8 = jobRepository.findByName("클라우드 엔지니어") + .orElseGet(() -> jobService.create( + "클라우드 엔지니어", + "AWS, GCP, Azure 등 클라우드 환경에서 인프라를 설계·배포·운영하며, 서비스의 안정성과 확장성을 책임지는 직군입니다." + )); // 사이버 보안 전문가 - Job job9 = jobService.create( - "보안 엔지니어", - "시스템과 네트워크의 보안 취약점을 점검하고, 공격 방어 및 보안 정책을 설계하는 역할을 수행합니다." - ); + Job job9 = jobRepository.findByName("보안 엔지니어") + .orElseGet(() -> jobService.create( + "보안 엔지니어", + "시스템과 네트워크의 보안 취약점을 점검하고, 공격 방어 및 보안 정책을 설계하는 역할을 수행합니다." + )); // 게임 서버/클라이언트 개발자 - Job job10 = jobService.create( - "게임 개발자", - "게임 클라이언트 또는 서버를 개발하는 직군으로, 그래픽·물리 엔진·네트워크 프로그래밍 등 다양한 기술을 다룹니다." - ); + Job job10 = jobRepository.findByName("게임 개발자") + .orElseGet(() -> jobService.create( + "게임 개발자", + "게임 클라이언트 또는 서버를 개발하는 직군으로, 그래픽·물리 엔진·네트워크 프로그래밍 등 다양한 기술을 다룹니다." + )); // QA / 테스트 엔지니어 - Job job11 = jobService.create( - "QA 엔지니어", - "소프트웨어 품질을 보증하기 위해 테스트를 설계·자동화·수행하는 직군으로, 버그 탐지와 품질 관리 프로세스를 담당합니다." - ); + Job job11 = jobRepository.findByName("QA 엔지니어") + .orElseGet(() -> jobService.create( + "QA 엔지니어", + "소프트웨어 품질을 보증하기 위해 테스트를 설계·자동화·수행하는 직군으로, 버그 탐지와 품질 관리 프로세스를 담당합니다." + )); } // --- Task 초기화 (기존 + 기초 보강) --- @@ -582,7 +592,7 @@ private Mentor updateMentorJob(Member member, Job job) { Mentor updatedMentor = Mentor.builder() .member(mentor.getMember()) - .jobId(job.getId()) + .job(job) .careerYears(mentor.getCareerYears()) .rate(mentor.getRate()) .build(); diff --git a/back/src/test/java/com/back/domain/member/member/controller/AdminMemberControllerTest.java b/back/src/test/java/com/back/domain/member/member/controller/AdminMemberControllerTest.java index 403bd41c..2d76b43e 100644 --- a/back/src/test/java/com/back/domain/member/member/controller/AdminMemberControllerTest.java +++ b/back/src/test/java/com/back/domain/member/member/controller/AdminMemberControllerTest.java @@ -1,26 +1,25 @@ package com.back.domain.member.member.controller; +import com.back.domain.member.member.dto.MenteeUpdateRequest; +import com.back.domain.member.member.dto.MentorUpdateRequest; import com.back.domain.member.member.entity.Member; import com.back.domain.member.member.service.MemberService; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; 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.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.transaction.annotation.Transactional; -import org.springframework.http.MediaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.back.domain.member.member.dto.MentorUpdateRequest; -import com.back.domain.member.member.dto.MenteeUpdateRequest; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static org.junit.jupiter.api.Assertions.*; @ActiveProfiles("test") @SpringBootTest @@ -104,8 +103,10 @@ void t4() throws Exception { result .andExpect(status().isOk()) .andExpect(jsonPath("$.data.role").value("MENTEE")) - .andExpect(jsonPath("$.data.interestedField").exists()) // 희망직업 필드 존재 확인 - .andExpect(jsonPath("$.data.careerYears").doesNotExist()); // 멘티는 연차 없음 + .andExpect(jsonPath("$.data.job").value("Backend")) // 희망직업 확인 + .andExpect(jsonPath("$.data.careerYears").doesNotExist()) // 멘티는 연차 없음 + .andExpect(jsonPath("$.data.menteeId").exists()) // 멘티 ID 존재 확인 + .andExpect(jsonPath("$.data.mentorId").doesNotExist()); // 멘토 ID는 없음 } @Test @@ -124,9 +125,10 @@ void t5() throws Exception { result .andExpect(status().isOk()) .andExpect(jsonPath("$.data.role").value("MENTOR")) - .andExpect(jsonPath("$.data.career").exists()) // 직업 필드 존재 확인 + .andExpect(jsonPath("$.data.job").value("Backend")) // 직업 확인 .andExpect(jsonPath("$.data.careerYears").value(5)) // 연차 확인 - .andExpect(jsonPath("$.data.interestedField").doesNotExist()); // 멘토는 희망직업 없음 + .andExpect(jsonPath("$.data.mentorId").exists()) // 멘토 ID 존재 확인 + .andExpect(jsonPath("$.data.menteeId").doesNotExist()); // 멘티 ID는 없음 } @Test diff --git a/back/src/test/java/com/back/domain/member/member/controller/MemberAuthControllerTest.java b/back/src/test/java/com/back/domain/member/member/controller/MemberAuthControllerTest.java index d81e3768..c91aade5 100644 --- a/back/src/test/java/com/back/domain/member/member/controller/MemberAuthControllerTest.java +++ b/back/src/test/java/com/back/domain/member/member/controller/MemberAuthControllerTest.java @@ -229,11 +229,11 @@ void t4() throws Exception { } @Test - @DisplayName("멘티 로그인 후 /auth/me로 정보 조회 - rq.getActor() role 확인") + @DisplayName("멘티 로그인 후 /auth/me로 정보 조회 - role 및 menteeId 확인") void t5() throws Exception { // 멘티 회원가입 String email = "mentee@example.com"; - memberService.joinMentee(email, "멘티사용자", "멘티닉네임", "password123", "Backend"); + Member member = memberService.joinMentee(email, "멘티사용자", "멘티닉네임", "password123", "Backend"); // 로그인하여 쿠키 받기 ResultActions loginResult = mvc.perform( @@ -250,7 +250,7 @@ void t5() throws Exception { // 로그인 응답에서 쿠키 추출 Cookie accessToken = loginResult.andReturn().getResponse().getCookie("accessToken"); - // /auth/me 호출하여 role 확인 (쿠키 포함) + // /auth/me 호출하여 role 및 menteeId 확인 (쿠키 포함) ResultActions result = mvc .perform(get("/auth/me") .cookie(accessToken)) @@ -258,7 +258,50 @@ void t5() throws Exception { result .andExpect(status().is2xxSuccessful()) - .andExpect(jsonPath("$.data.role").value("MENTEE")); + .andExpect(jsonPath("$.data.role").value("MENTEE")) + .andExpect(jsonPath("$.data.memberId").value(member.getId())) + .andExpect(jsonPath("$.data.email").value(email)) + .andExpect(jsonPath("$.data.nickname").value("멘티닉네임")) + .andExpect(jsonPath("$.data.mentorId").isEmpty()) + .andExpect(jsonPath("$.data.menteeId").isNotEmpty()); + } + + @Test + @DisplayName("멘토 로그인 후 /auth/me로 정보 조회 - role 및 mentorId 확인") + void t5_1() throws Exception { + // 멘토 회원가입 + String email = "mentor@example.com"; + Member member = memberService.joinMentor(email, "멘토사용자", "멘토닉네임", "password123", "Backend", 5); + + // 로그인하여 쿠키 받기 + ResultActions loginResult = mvc.perform( + post("/auth/login") + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "email": "%s", + "password": "password123" + } + """, email)) + ); + + // 로그인 응답에서 쿠키 추출 + Cookie accessToken = loginResult.andReturn().getResponse().getCookie("accessToken"); + + // /auth/me 호출하여 role 및 mentorId 확인 (쿠키 포함) + ResultActions result = mvc + .perform(get("/auth/me") + .cookie(accessToken)) + .andDo(print()); + + result + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$.data.role").value("MENTOR")) + .andExpect(jsonPath("$.data.memberId").value(member.getId())) + .andExpect(jsonPath("$.data.email").value(email)) + .andExpect(jsonPath("$.data.nickname").value("멘토닉네임")) + .andExpect(jsonPath("$.data.mentorId").isNotEmpty()) + .andExpect(jsonPath("$.data.menteeId").isEmpty()); } @Test diff --git a/back/src/test/java/com/back/domain/member/member/controller/MemberMyPageControllerTest.java b/back/src/test/java/com/back/domain/member/member/controller/MemberMyPageControllerTest.java index fcefa3d4..31dd3b3e 100644 --- a/back/src/test/java/com/back/domain/member/member/controller/MemberMyPageControllerTest.java +++ b/back/src/test/java/com/back/domain/member/member/controller/MemberMyPageControllerTest.java @@ -63,7 +63,8 @@ void t1() throws Exception { .andExpect(jsonPath("$.msg").value("멘티 정보 조회 성공")) .andExpect(jsonPath("$.data.email").value(email)) .andExpect(jsonPath("$.data.name").value("멘티유저")) - .andExpect(jsonPath("$.data.nickname").value("멘티닉네임")); + .andExpect(jsonPath("$.data.nickname").value("멘티닉네임")) + .andExpect(jsonPath("$.data.job").value("Backend")); // 희망직업 필드 확인 } @Test @@ -108,6 +109,19 @@ void t2() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.resultCode").value("200-10")) .andExpect(jsonPath("$.msg").value("멘티 정보 수정 성공")); + + // 수정 후 다시 조회하여 job이 변경되었는지 확인 + ResultActions getResult = mvc + .perform( + get("/members/me/mentee") + .cookie(accessToken) + ) + .andDo(print()); + + getResult + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.nickname").value("새로운닉네임")) + .andExpect(jsonPath("$.data.job").value("Mobile")); // job이 변경되었는지 확인 } @Test @@ -148,6 +162,7 @@ void t3() throws Exception { .andExpect(jsonPath("$.data.email").value(email)) .andExpect(jsonPath("$.data.name").value("멘토유저")) .andExpect(jsonPath("$.data.nickname").value("멘토닉네임")) + .andExpect(jsonPath("$.data.job").value("Backend")) // 직업 필드 확인 .andExpect(jsonPath("$.data.careerYears").value(5)); } @@ -194,6 +209,20 @@ void t4() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.resultCode").value("200-12")) .andExpect(jsonPath("$.msg").value("멘토 정보 수정 성공")); + + // 수정 후 다시 조회하여 job과 careerYears가 변경되었는지 확인 + ResultActions getResult = mvc + .perform( + get("/members/me/mentor") + .cookie(accessToken) + ) + .andDo(print()); + + getResult + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.nickname").value("새로운멘토닉네임")) + .andExpect(jsonPath("$.data.job").value("Fullstack")) // job이 변경되었는지 확인 + .andExpect(jsonPath("$.data.careerYears").value(7)); // careerYears가 변경되었는지 확인 } @Test diff --git a/back/src/test/java/com/back/fixture/JobFixture.java b/back/src/test/java/com/back/fixture/JobFixture.java new file mode 100644 index 00000000..aa4dc8d8 --- /dev/null +++ b/back/src/test/java/com/back/fixture/JobFixture.java @@ -0,0 +1,56 @@ +package com.back.fixture; + +import com.back.domain.job.job.entity.Job; +import org.springframework.test.util.ReflectionTestUtils; + +public class JobFixture { + private String name = "백엔드 개발자"; + private String description = "서버 사이드 로직 구현과 데이터베이스를 담당하는 개발자"; + private Long id = null; + + private static JobFixture builder() { + return new JobFixture(); + } + + public static Job createDefault() { + return builder().build(); + } + + public static Job create(Long id, String name, String description) { + return builder() + .withId(id) + .withName(name) + .withDescription(description) + .build(); + } + + public static Job create(String name, String description) { + return builder() + .withName(name) + .withDescription(description) + .build(); + } + + public JobFixture withName(String name) { + this.name = name; + return this; + } + + public JobFixture withDescription(String description) { + this.description = description; + return this; + } + + public JobFixture withId(Long id) { + this.id = id; + return this; + } + + public Job build() { + Job job = new Job(name, description); + if (id != null) { + ReflectionTestUtils.setField(job, "id", id); + } + return job; + } +} diff --git a/back/src/test/java/com/back/fixture/MemberTestFixture.java b/back/src/test/java/com/back/fixture/MemberTestFixture.java index fadbee19..2ceda602 100644 --- a/back/src/test/java/com/back/fixture/MemberTestFixture.java +++ b/back/src/test/java/com/back/fixture/MemberTestFixture.java @@ -1,5 +1,7 @@ package com.back.fixture; +import com.back.domain.job.job.entity.Job; +import com.back.domain.job.job.repository.JobRepository; import com.back.domain.member.member.entity.Member; import com.back.domain.member.member.repository.MemberRepository; import com.back.domain.member.mentee.entity.Mentee; @@ -17,6 +19,7 @@ public class MemberTestFixture { @Autowired private MentorRepository mentorRepository; @Autowired private MenteeRepository menteeRepository; @Autowired private PasswordEncoder passwordEncoder; + @Autowired private JobRepository jobRepository; private int counter = 0; @@ -50,12 +53,24 @@ public Member createMenteeMember() { } + // ===== Job ===== + + public Job createJob(String name, String description) { + return jobRepository.findByName(name) + .orElseGet(() -> jobRepository.save(new Job(name, description))); + } + + public Job createDefaultJob() { + return createJob("백엔드 개발자", "서버 사이드 로직 구현과 데이터베이스를 담당하는 개발자"); + } + + // ===== Mentor ===== - public Mentor createMentor(Member member, Long jobId, Double rate, Integer careerYears) { + public Mentor createMentor(Member member, Job job, Double rate, Integer careerYears) { Mentor mentor = Mentor.builder() .member(member) - .jobId(jobId) + .job(job) .rate(rate) .careerYears(careerYears) .build(); @@ -63,21 +78,23 @@ public Mentor createMentor(Member member, Long jobId, Double rate, Integer caree } public Mentor createMentor(Member member) { - return createMentor(member, 1L, 4.5, 5); + Job job = createDefaultJob(); + return createMentor(member, job, 4.5, 5); } // ===== Mentee ===== - public Mentee createMentee(Member member, Long jobId) { + public Mentee createMentee(Member member, Job job) { Mentee mentee = Mentee.builder() .member(member) - .jobId(jobId) + .job(job) .build(); return menteeRepository.save(mentee); } public Mentee createMentee(Member member) { - return createMentee(member, 1L); + Job job = createDefaultJob(); + return createMentee(member, job); } } \ No newline at end of file diff --git a/back/src/test/java/com/back/fixture/MenteeFixture.java b/back/src/test/java/com/back/fixture/MenteeFixture.java index 83164a17..108484cc 100644 --- a/back/src/test/java/com/back/fixture/MenteeFixture.java +++ b/back/src/test/java/com/back/fixture/MenteeFixture.java @@ -1,34 +1,33 @@ package com.back.fixture; +import com.back.domain.job.job.entity.Job; import com.back.domain.member.member.entity.Member; import com.back.domain.member.mentee.entity.Mentee; import org.springframework.test.util.ReflectionTestUtils; public class MenteeFixture { - private static final Long DEFAULT_JOB_ID = 1L; - public static Mentee create(Member member) { return Mentee.builder() .member(member) - .jobId(DEFAULT_JOB_ID) + .job(JobFixture.createDefault()) .build(); } public static Mentee create(Long id, Member member) { Mentee mentee = Mentee.builder() .member(member) - .jobId(DEFAULT_JOB_ID) + .job(JobFixture.createDefault()) .build(); ReflectionTestUtils.setField(mentee, "id", id); return mentee; } - public static Mentee create(Long id, Member member, Long jobId) { + public static Mentee create(Long id, Member member, Job job) { Mentee mentee = Mentee.builder() .member(member) - .jobId(jobId) + .job(job) .build(); ReflectionTestUtils.setField(mentee, "id", id); diff --git a/back/src/test/java/com/back/fixture/MentorFixture.java b/back/src/test/java/com/back/fixture/MentorFixture.java index ed54f94f..9a1c6e5d 100644 --- a/back/src/test/java/com/back/fixture/MentorFixture.java +++ b/back/src/test/java/com/back/fixture/MentorFixture.java @@ -1,19 +1,19 @@ package com.back.fixture; +import com.back.domain.job.job.entity.Job; import com.back.domain.member.member.entity.Member; import com.back.domain.member.mentor.entity.Mentor; import org.springframework.test.util.ReflectionTestUtils; public class MentorFixture { - private static final Long DEFAULT_JOB_ID = 1L; private static final Double DEFAULT_RATE = 4.5; private static final Integer DEFAULT_CAREER_YEARS = 5; public static Mentor create(Member member) { return Mentor.builder() .member(member) - .jobId(DEFAULT_JOB_ID) + .job(JobFixture.createDefault()) .rate(DEFAULT_RATE) .careerYears(DEFAULT_CAREER_YEARS) .build(); @@ -22,7 +22,7 @@ public static Mentor create(Member member) { public static Mentor create(Long id, Member member) { Mentor mentor = Mentor.builder() .member(member) - .jobId(DEFAULT_JOB_ID) + .job(JobFixture.createDefault()) .rate(DEFAULT_RATE) .careerYears(DEFAULT_CAREER_YEARS) .build(); @@ -31,10 +31,10 @@ public static Mentor create(Long id, Member member) { return mentor; } - public static Mentor create(Long id, Member member, Long jobId, Double rate, Integer careerYears) { + public static Mentor create(Long id, Member member, Job job, Double rate, Integer careerYears) { Mentor mentor = Mentor.builder() .member(member) - .jobId(jobId) + .job(job) .rate(rate) .careerYears(careerYears) .build(); From 204b837b48258b25b479d810a54176ad719af76d Mon Sep 17 00:00:00 2001 From: sso0om Date: Mon, 13 Oct 2025 14:37:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Refactor:=20=EB=A9=98=ED=86=A0=EB=A7=81=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=97=90=20memberId=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor: 응답에 memberId 추가 * Refactor: 멘티는 현재일 이후 슬롯만 조회 가능 * Refactor: 나의 슬롯은 현재일 이전이고 예약 이력이 없을 경우 expired 로 세팅 --- .../com/back/domain/member/mentee/dto/MenteeDto.java | 3 +++ .../domain/member/mentor/dto/MentorDetailDto.java | 3 +++ .../com/back/domain/member/mentor/dto/MentorDto.java | 3 +++ .../mentoring/dto/MentoringWithTagsDto.java | 3 +++ .../mentoring/dto/response/ReviewResponse.java | 3 +++ .../mentoring/slot/constant/MentorSlotStatus.java | 3 ++- .../mentoring/slot/dto/response/MentorSlotDto.java | 2 ++ .../slot/repository/MentorSlotRepository.java | 10 +++++++++- .../slot/service/MentorSlotServiceTest.java | 12 +++++++----- 9 files changed, 35 insertions(+), 7 deletions(-) diff --git a/back/src/main/java/com/back/domain/member/mentee/dto/MenteeDto.java b/back/src/main/java/com/back/domain/member/mentee/dto/MenteeDto.java index a6f837bd..76988b20 100644 --- a/back/src/main/java/com/back/domain/member/mentee/dto/MenteeDto.java +++ b/back/src/main/java/com/back/domain/member/mentee/dto/MenteeDto.java @@ -6,12 +6,15 @@ public record MenteeDto( @Schema(description = "멘티 ID") Long menteeId, + @Schema(description = "멘티 회원 ID") + Long menteeMemberId, @Schema(description = "멘티 닉네임") String nickname ) { public static MenteeDto from(Mentee mentee) { return new MenteeDto( mentee.getId(), + mentee.getMember().getId(), mentee.getMember().getNickname() ); } diff --git a/back/src/main/java/com/back/domain/member/mentor/dto/MentorDetailDto.java b/back/src/main/java/com/back/domain/member/mentor/dto/MentorDetailDto.java index 4c4e27c0..e0639fae 100644 --- a/back/src/main/java/com/back/domain/member/mentor/dto/MentorDetailDto.java +++ b/back/src/main/java/com/back/domain/member/mentor/dto/MentorDetailDto.java @@ -6,6 +6,8 @@ public record MentorDetailDto( @Schema(description = "멘토 ID") Long mentorId, + @Schema(description = "멘토 회원 ID") + Long mentorMemberId, @Schema(description = "멘토 닉네임") String nickname, @Schema(description = "평점") @@ -17,6 +19,7 @@ public record MentorDetailDto( public static MentorDetailDto from(Mentor mentor) { return new MentorDetailDto( mentor.getId(), + mentor.getMember().getId(), mentor.getMember().getNickname(), mentor.getRate(), mentor.getCareerYears() diff --git a/back/src/main/java/com/back/domain/member/mentor/dto/MentorDto.java b/back/src/main/java/com/back/domain/member/mentor/dto/MentorDto.java index d25fb2ec..88a6063b 100644 --- a/back/src/main/java/com/back/domain/member/mentor/dto/MentorDto.java +++ b/back/src/main/java/com/back/domain/member/mentor/dto/MentorDto.java @@ -6,12 +6,15 @@ public record MentorDto( @Schema(description = "멘토 ID") Long mentorId, + @Schema(description = "멘토 회원 ID") + Long mentorMemberId, @Schema(description = "멘토 닉네임") String nickname ) { public static MentorDto from(Mentor mentor) { return new MentorDto( mentor.getId(), + mentor.getMember().getId(), mentor.getMember().getNickname() ); } diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java index 1331f751..67e2f730 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/MentoringWithTagsDto.java @@ -14,6 +14,8 @@ public record MentoringWithTagsDto( List tags, @Schema(description = "멘토 ID") Long mentorId, + @Schema(description = "멘토 회원 ID") + Long mentorMemberId, @Schema(description = "멘토 닉네임") String nickname ) { @@ -23,6 +25,7 @@ public static MentoringWithTagsDto from(Mentoring mentoring) { mentoring.getTitle(), mentoring.getTagNames(), mentoring.getMentor().getId(), + mentoring.getMentor().getMember().getId(), mentoring.getMentor().getMember().getNickname() ); } diff --git a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/response/ReviewResponse.java b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/response/ReviewResponse.java index 99291a55..c80f7e89 100644 --- a/back/src/main/java/com/back/domain/mentoring/mentoring/dto/response/ReviewResponse.java +++ b/back/src/main/java/com/back/domain/mentoring/mentoring/dto/response/ReviewResponse.java @@ -19,6 +19,8 @@ public record ReviewResponse( @Schema(description = "멘티 ID") Long menteeId, + @Schema(description = "멘티 회원 ID") + Long menteeMemberId, @Schema(description = "멘티 닉네임") String menteeNickname ) { @@ -30,6 +32,7 @@ public static ReviewResponse from(Review review) { review.getCreateDate(), review.getModifyDate(), review.getMentee().getId(), + review.getMentee().getMember().getId(), review.getMentee().getMember().getNickname() ); } diff --git a/back/src/main/java/com/back/domain/mentoring/slot/constant/MentorSlotStatus.java b/back/src/main/java/com/back/domain/mentoring/slot/constant/MentorSlotStatus.java index 13d16388..a13e097b 100644 --- a/back/src/main/java/com/back/domain/mentoring/slot/constant/MentorSlotStatus.java +++ b/back/src/main/java/com/back/domain/mentoring/slot/constant/MentorSlotStatus.java @@ -4,5 +4,6 @@ public enum MentorSlotStatus { AVAILABLE, // 예약 가능 PENDING, // 예약 승인 대기 APPROVED, // 예약 승인됨(확정) - COMPLETED // 멘토링 완료 + COMPLETED, // 멘토링 완료 + EXPIRED // 만료 } diff --git a/back/src/main/java/com/back/domain/mentoring/slot/dto/response/MentorSlotDto.java b/back/src/main/java/com/back/domain/mentoring/slot/dto/response/MentorSlotDto.java index 2185b0ec..6644a768 100644 --- a/back/src/main/java/com/back/domain/mentoring/slot/dto/response/MentorSlotDto.java +++ b/back/src/main/java/com/back/domain/mentoring/slot/dto/response/MentorSlotDto.java @@ -10,6 +10,8 @@ public record MentorSlotDto( Long mentorSlotId, @Schema(description = "멘토 ID") Long mentorId, + @Schema(description = "멘토 회원 ID") + Long mentorMemberId, @Schema(description = "시작 일시") LocalDateTime startDateTime, @Schema(description = "종료 일시") diff --git a/back/src/main/java/com/back/domain/mentoring/slot/repository/MentorSlotRepository.java b/back/src/main/java/com/back/domain/mentoring/slot/repository/MentorSlotRepository.java index f97856e6..82c73db5 100644 --- a/back/src/main/java/com/back/domain/mentoring/slot/repository/MentorSlotRepository.java +++ b/back/src/main/java/com/back/domain/mentoring/slot/repository/MentorSlotRepository.java @@ -21,9 +21,15 @@ public interface MentorSlotRepository extends JpaRepository { SELECT new com.back.domain.mentoring.slot.dto.response.MentorSlotDto( ms.id, ms.mentor.id, + ms.mentor.member.id, ms.startDateTime, ms.endDateTime, - ms.status, + CASE + WHEN ms.startDateTime < CURRENT_TIMESTAMP + AND ms.status = com.back.domain.mentoring.slot.constant.MentorSlotStatus.AVAILABLE + THEN com.back.domain.mentoring.slot.constant.MentorSlotStatus.EXPIRED + ELSE ms.status + END, r.id ) FROM MentorSlot ms @@ -45,6 +51,7 @@ List findMySlots( SELECT new com.back.domain.mentoring.slot.dto.response.MentorSlotDto( ms.id, ms.mentor.id, + ms.mentor.member.id, ms.startDateTime, ms.endDateTime, ms.status, @@ -53,6 +60,7 @@ List findMySlots( FROM MentorSlot ms WHERE ms.mentor.id = :mentorId AND ms.status = 'AVAILABLE' + AND ms.startDateTime >= CURRENT_TIMESTAMP AND ms.startDateTime < :end AND ms.endDateTime >= :start ORDER BY ms.startDateTime ASC diff --git a/back/src/test/java/com/back/domain/mentoring/slot/service/MentorSlotServiceTest.java b/back/src/test/java/com/back/domain/mentoring/slot/service/MentorSlotServiceTest.java index 7995d3cc..7c3870e9 100644 --- a/back/src/test/java/com/back/domain/mentoring/slot/service/MentorSlotServiceTest.java +++ b/back/src/test/java/com/back/domain/mentoring/slot/service/MentorSlotServiceTest.java @@ -88,23 +88,24 @@ void getMyMentorSlots() { LocalDate base = LocalDate.now().plusMonths(1); LocalDateTime startDate = base.atStartOfDay(); LocalDateTime endDate = base.withDayOfMonth(base.lengthOfMonth()).atTime(23, 59); + Long memberId = mentor1.getMember().getId(); MentorSlotDto slotDto1 = new MentorSlotDto( - 1L, mentor1.getId(), + 1L, mentor1.getId(), memberId, mentorSlot1.getStartDateTime(), mentorSlot1.getEndDateTime(), MentorSlotStatus.AVAILABLE, null ); MentorSlotDto slotDto2 = new MentorSlotDto( - 2L, mentor1.getId(), + 2L, mentor1.getId(), memberId, base.withDayOfMonth(2).atTime(10, 0), base.withDayOfMonth(2).atTime(11, 0), MentorSlotStatus.AVAILABLE, null ); MentorSlotDto slotDto3 = new MentorSlotDto( - 3L, mentor1.getId(), + 3L, mentor1.getId(), memberId, base.withDayOfMonth(15).atTime(14, 0), base.withDayOfMonth(15).atTime(15, 0), MentorSlotStatus.AVAILABLE, @@ -155,16 +156,17 @@ void getAvailableMentorSlots() { LocalDate base = LocalDate.now().plusMonths(1); LocalDateTime startDate = base.atStartOfDay(); LocalDateTime endDate = base.withDayOfMonth(base.lengthOfMonth()).atTime(23, 59); + Long memberId = mentor1.getMember().getId(); MentorSlotDto slotDto1 = new MentorSlotDto( - 1L, mentor1.getId(), + 1L, mentor1.getId(), memberId, mentorSlot1.getStartDateTime(), mentorSlot1.getEndDateTime(), MentorSlotStatus.AVAILABLE, null ); MentorSlotDto slotDto2 = new MentorSlotDto( - 2L, mentor1.getId(), + 2L, mentor1.getId(), memberId, base.withDayOfMonth(2).atTime(10, 0), base.withDayOfMonth(2).atTime(11, 0), MentorSlotStatus.AVAILABLE,