diff --git a/src/main/java/org/sopt/seonyakServer/domain/appointment/controller/AppointmentController.java b/src/main/java/org/sopt/seonyakServer/domain/appointment/controller/AppointmentController.java index 9d6777f..80ed962 100644 --- a/src/main/java/org/sopt/seonyakServer/domain/appointment/controller/AppointmentController.java +++ b/src/main/java/org/sopt/seonyakServer/domain/appointment/controller/AppointmentController.java @@ -5,9 +5,12 @@ import org.sopt.seonyakServer.domain.appointment.dto.AppointmentAcceptRequest; import org.sopt.seonyakServer.domain.appointment.dto.AppointmentRejectRequest; import org.sopt.seonyakServer.domain.appointment.dto.AppointmentRequest; +import org.sopt.seonyakServer.domain.appointment.dto.GoogleMeetLinkResponse; import org.sopt.seonyakServer.domain.appointment.service.AppointmentService; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -15,12 +18,12 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/api/v1/appoinment") +@RequestMapping("/api/v1") public class AppointmentController { private final AppointmentService appointmentService; - @PostMapping("") + @PostMapping("/appointment") public ResponseEntity postAppointment( @RequestBody final AppointmentRequest appointmentRequest ) { @@ -43,4 +46,11 @@ public ResponseEntity rejectAppointment( appointmentService.rejectAppointment(appointmentRejectRequest); return ResponseEntity.ok().build(); } + + @GetMapping("/google-meet/{appointmentId}") + public ResponseEntity getGoogleMeetLink( + @PathVariable final Long appointmentId + ) { + return ResponseEntity.ok(appointmentService.getGoogleMeetLink(appointmentId)); + } } diff --git a/src/main/java/org/sopt/seonyakServer/domain/appointment/dto/GoogleMeetLinkResponse.java b/src/main/java/org/sopt/seonyakServer/domain/appointment/dto/GoogleMeetLinkResponse.java new file mode 100644 index 0000000..608351b --- /dev/null +++ b/src/main/java/org/sopt/seonyakServer/domain/appointment/dto/GoogleMeetLinkResponse.java @@ -0,0 +1,9 @@ +package org.sopt.seonyakServer.domain.appointment.dto; + +public record GoogleMeetLinkResponse( + String googleMeetLink +) { + public static GoogleMeetLinkResponse of(final String googleMeetLink) { + return new GoogleMeetLinkResponse(googleMeetLink); + } +} diff --git a/src/main/java/org/sopt/seonyakServer/domain/appointment/service/AppointmentService.java b/src/main/java/org/sopt/seonyakServer/domain/appointment/service/AppointmentService.java index a5aab51..87614a1 100644 --- a/src/main/java/org/sopt/seonyakServer/domain/appointment/service/AppointmentService.java +++ b/src/main/java/org/sopt/seonyakServer/domain/appointment/service/AppointmentService.java @@ -5,6 +5,7 @@ import org.sopt.seonyakServer.domain.appointment.dto.AppointmentAcceptRequest; import org.sopt.seonyakServer.domain.appointment.dto.AppointmentRejectRequest; import org.sopt.seonyakServer.domain.appointment.dto.AppointmentRequest; +import org.sopt.seonyakServer.domain.appointment.dto.GoogleMeetLinkResponse; import org.sopt.seonyakServer.domain.appointment.model.Appointment; import org.sopt.seonyakServer.domain.appointment.model.AppointmentStatus; import org.sopt.seonyakServer.domain.appointment.repository.AppointmentRepository; @@ -34,7 +35,7 @@ public void postAppointment(AppointmentRequest appointmentRequest) { if (member.getId().equals(senior.getId())) { throw new CustomException(ErrorType.SAME_MEMBER_APPOINTMENT_ERROR); } - Appointment appointment = Appointment.createAppointment( + Appointment appointment = Appointment.create( member, senior, AppointmentStatus.PENDING, @@ -48,10 +49,11 @@ public void postAppointment(AppointmentRequest appointmentRequest) { @Transactional public void acceptAppointment(AppointmentAcceptRequest appointmentAcceptRequest) { Appointment appointment = appointmentRepository.findAppointmentByIdOrThrow( - appointmentAcceptRequest.appointmentId()); + appointmentAcceptRequest.appointmentId() + ); Member member = memberRepository.findMemberByIdOrThrow(principalHandler.getUserIdFromPrincipal()); - // 약속의 선배ID와 토크ID가 일치하지 않는 경우 + // 약속의 선배 Id와 토큰 Id가 일치하지 않는 경우 if (!Objects.equals(member.getId(), appointment.getSenior().getId())) { throw new CustomException(ErrorType.NOT_AUTHORIZATION_ACCEPT); } @@ -67,10 +69,11 @@ public void acceptAppointment(AppointmentAcceptRequest appointmentAcceptRequest) @Transactional public void rejectAppointment(AppointmentRejectRequest appointmentRejectRequest) { Appointment appointment = appointmentRepository.findAppointmentByIdOrThrow( - appointmentRejectRequest.appointmentId()); + appointmentRejectRequest.appointmentId() + ); Member member = memberRepository.findMemberByIdOrThrow(principalHandler.getUserIdFromPrincipal()); - // 약속의 선배ID와 토크ID가 일치하지 않는 경우 + // 약속의 선배 Id와 토큰 Id가 일치하지 않는 경우 if (!Objects.equals(member.getId(), appointment.getSenior().getId())) { throw new CustomException(ErrorType.NOT_AUTHORIZATION_REJECT); } @@ -82,4 +85,25 @@ public void rejectAppointment(AppointmentRejectRequest appointmentRejectRequest) ); appointmentRepository.save(appointment); } -} \ No newline at end of file + + @Transactional(readOnly = true) + public GoogleMeetLinkResponse getGoogleMeetLink(Long appointmentId) { + Appointment appointment = appointmentRepository.findAppointmentByIdOrThrow(appointmentId); + + Long userId = memberRepository.findMemberByIdOrThrow(principalHandler.getUserIdFromPrincipal()).getId(); + Long memberId = appointment.getMember().getId(); + Long seniorMemberId = appointment.getSenior().getMember().getId(); + + if (!userId.equals(memberId) && !userId.equals(seniorMemberId)) { + throw new CustomException(ErrorType.NOT_MEMBERS_APPOINTMENT_ERROR); + } + + String googleMeetLink = appointment.getGoogleMeetLink(); + + if (googleMeetLink == null || googleMeetLink.isEmpty()) { + throw new CustomException(ErrorType.NOT_FOUND_GOOGLE_MEET_LINK_ERROR); + } + + return GoogleMeetLinkResponse.of(googleMeetLink); + } +} diff --git a/src/main/java/org/sopt/seonyakServer/domain/senior/service/SeniorService.java b/src/main/java/org/sopt/seonyakServer/domain/senior/service/SeniorService.java index 7ed3381..f93f181 100644 --- a/src/main/java/org/sopt/seonyakServer/domain/senior/service/SeniorService.java +++ b/src/main/java/org/sopt/seonyakServer/domain/senior/service/SeniorService.java @@ -25,7 +25,7 @@ public class SeniorService { @Transactional public String createSenior(final MemberJoinRequest memberJoinRequest, Member member) { - Senior senior = Senior.createSenior( + Senior senior = Senior.create( member, memberJoinRequest.businessCard(), memberJoinRequest.detailPosition(), diff --git a/src/main/java/org/sopt/seonyakServer/global/auth/PrincipalHandler.java b/src/main/java/org/sopt/seonyakServer/global/auth/PrincipalHandler.java index 9d091ef..ab92b83 100644 --- a/src/main/java/org/sopt/seonyakServer/global/auth/PrincipalHandler.java +++ b/src/main/java/org/sopt/seonyakServer/global/auth/PrincipalHandler.java @@ -20,7 +20,7 @@ public void isPrincipalNull( final Object principal ) { if (principal.toString().equals(ANONYMOUS_USER)) { - throw new CustomException(ErrorType.EMPTY_PRINCIPLE_ERROR); + throw new CustomException(ErrorType.EMPTY_PRINCIPAL_ERROR); } } } \ No newline at end of file diff --git a/src/main/java/org/sopt/seonyakServer/global/exception/enums/ErrorType.java b/src/main/java/org/sopt/seonyakServer/global/exception/enums/ErrorType.java index 120164a..395c819 100644 --- a/src/main/java/org/sopt/seonyakServer/global/exception/enums/ErrorType.java +++ b/src/main/java/org/sopt/seonyakServer/global/exception/enums/ErrorType.java @@ -32,13 +32,15 @@ public enum ErrorType { JSON_TO_MAP_ERROR(HttpStatus.BAD_REQUEST, "40017", "JSON 문자열을 Map으로 변환하는 중 오류가 발생했습니다."), UNIV_CERT_REQUEST_ERROR(HttpStatus.BAD_REQUEST, "40018", "이미 인증이 완료된 이메일입니다."), SAME_MEMBER_APPOINTMENT_ERROR(HttpStatus.BAD_REQUEST, "40019", "자기 자신에게는 약속을 신청할 수 없습니다."), + NOT_MEMBERS_APPOINTMENT_ERROR(HttpStatus.BAD_REQUEST, "40020", "해당 회원의 약속이 아닙니다"), + // S3 관련 오류 IMAGE_EXTENSION_ERROR(HttpStatus.BAD_REQUEST, "40051", "이미지 확장자는 jpg, png, webp만 가능합니다."), IMAGE_SIZE_ERROR(HttpStatus.BAD_REQUEST, "40052", "이미지 사이즈는 5MB를 넘을 수 없습니다."), // 인증 관련 오류 - EMPTY_PRINCIPLE_ERROR(HttpStatus.BAD_REQUEST, "40076", "Principle 객체가 없습니다. (null)"), + EMPTY_PRINCIPAL_ERROR(HttpStatus.BAD_REQUEST, "40076", "Principal 객체가 없습니다. (null)"), /** * 401 UNAUTHORIZED @@ -50,7 +52,7 @@ public enum ErrorType { INVALID_JWT_SIGNATURE(HttpStatus.UNAUTHORIZED, "40105", "잘못된 JWT 서명입니다."), UNKNOWN_JWT_ERROR(HttpStatus.UNAUTHORIZED, "40106", "알 수 없는 JWT 토큰 오류가 발생했습니다."), - INVALID_SOCIAL_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "40107", "유효하지 않은 소셜 엑세스 토큰입니다."), + INVALID_SOCIAL_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "40107", "유효하지 않은 소셜 액세스 토큰입니다."), EXPIRED_AUTHENTICATION_CODE(HttpStatus.UNAUTHORIZED, "40108", "인가 코드가 만료되었습니다."), UN_LOGIN_ERROR(HttpStatus.UNAUTHORIZED, "40109", "로그인 후 진행해주세요."), NOT_AUTHORIZATION_ACCEPT(HttpStatus.UNAUTHORIZED, "40110", "약속을 수락할 권한이 없습니다."), @@ -65,6 +67,7 @@ public enum ErrorType { NOT_FOUND_SENIOR_BY_MEMBER(HttpStatus.NOT_FOUND, "40404", "해당 ID를 가진 멤버와 매핑된 선배를 찾을 수 없습니다."), NOT_FOUND_SENIOR_ERROR(HttpStatus.NOT_FOUND, "40405", "존재하지 않는 선배입니다."), NOT_FOUND_APPOINTMENT_ERROR(HttpStatus.NOT_FOUND, "40406", "존재하지 않는 약속입니다."), + NOT_FOUND_GOOGLE_MEET_LINK_ERROR(HttpStatus.NOT_FOUND, "40407", "구글 미트 링크가 존재하지 않는 약속입니다."), /** * 409 CONFLICT