Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package es.princip.ringus.application.mentoring;

import es.princip.ringus.domain.exception.MenteeErrorCode;
import es.princip.ringus.domain.exception.MentorErrorCode;
import es.princip.ringus.domain.member.MemberRepository;
import es.princip.ringus.domain.mentee.Mentee;
import es.princip.ringus.domain.mentee.MenteeRepository;
import es.princip.ringus.domain.mentor.Mentor;
import es.princip.ringus.domain.mentor.MentorRepository;
import es.princip.ringus.domain.mentoring.Mentoring;
import es.princip.ringus.domain.mentoring.MentoringRepository;
import es.princip.ringus.domain.mentoring.MentoringStatus;
import es.princip.ringus.global.exception.CustomRuntimeException;
import es.princip.ringus.presentation.mentoring.dto.CreateMentoringRequest;
import es.princip.ringus.presentation.mentoring.dto.MentoringResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MentoringService {
private final MentoringRepository mentoringRepository;
private final MentorRepository mentorRepository;
private final MenteeRepository menteeRepository;

/**
* 멘토링 신청 생성
*/
@Transactional
public MentoringResponse createMentoring(CreateMentoringRequest request, Long memberId) {
Mentor mentor = mentorRepository.findById(request.mentorId())
.orElseThrow(() -> new CustomRuntimeException(MentorErrorCode.MENTOR_NOT_FOUND));
Mentee mentee = menteeRepository.findByMemberId(memberId)
.orElseThrow(() -> new CustomRuntimeException(MenteeErrorCode.MENTEE_NOT_FOUND));
final Mentoring mentoring = Mentoring.of(MentoringStatus.WAITING, request.topic(), request.applyTimes(), request.mentoringMessage(), mentor, mentee);
return MentoringResponse.from(mentoringRepository.save(mentoring));

}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package es.princip.ringus.domain.exception;

import es.princip.ringus.global.exception.ErrorCode;
import org.springframework.http.HttpStatus;

public enum MentoringErrorCode implements ErrorCode {

MENTORING_NOT_FOUND(HttpStatus.NOT_FOUND,"해당 신청이 존재하지 않음"),
MENTORING_TIME_CONVERT_ERROR(HttpStatus.NOT_MODIFIED,"신청 시간 변환 오류"),
MENTORING_TIME_ERROR(HttpStatus.BAD_REQUEST,"신청 가능 시간 오류");

MentoringErrorCode(HttpStatus status , String message) {
this.status = status;
this.message = message;
}

private final HttpStatus status;
private final String message;


@Override
public HttpStatus status() {
return this.status;
}

@Override
public String message() {
return this.message;
}

@Override
public String code() {
return this.name();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface MenteeRepository extends JpaRepository<Mentee, Long> {
Optional<Mentee> findByMemberId(Long memberId);
boolean existsByMemberId(Long memberId);

@Query("SELECT m.profileImage FROM Mentee m WHERE m.memberId = :memberId")
Expand Down
90 changes: 90 additions & 0 deletions src/main/java/es/princip/ringus/domain/mentoring/Mentoring.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package es.princip.ringus.domain.mentoring;

import es.princip.ringus.domain.mentoring.converter.MentoringTimeConverter;
import es.princip.ringus.domain.base.BaseTimeEntity;
import es.princip.ringus.domain.exception.MentoringErrorCode;
import es.princip.ringus.domain.mentee.Mentee;
import es.princip.ringus.domain.mentor.Mentor;
import es.princip.ringus.global.exception.CustomRuntimeException;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Entity
@Getter
@Table(name = "mentoring")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Mentoring extends BaseTimeEntity {

@Id
@Column(name = "mentoring_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Enumerated(EnumType.STRING)
private MentoringStatus status;

@Enumerated(EnumType.STRING)
private MentoringTopic topic;

@Convert(converter = MentoringTimeConverter.class)
@Column(length = 500, nullable = false) // 500자 이내
//@Column(columnDefinition = "TEXT") // 500자 초과 시
private List<MentoringTime> applyTimes;

@Column(length = 500, nullable = false)
private String mentoringMessage; // 상담 메시지 추가 (최대 500자)

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "mentor_id", nullable = false)
private Mentor mentor;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "mentee_id", nullable = false)
private Mentee mentee;

@Builder
public Mentoring(
final MentoringStatus status,
final MentoringTopic topic,
final List<MentoringTime> applyTimes,
final String mentoringMessage,
final Mentor mentor,
final Mentee mentee
) {
if (applyTimes == null || applyTimes.isEmpty()) {
throw new CustomRuntimeException(MentoringErrorCode.MENTORING_TIME_ERROR);
}
if (applyTimes.size() > 5) {
throw new CustomRuntimeException(MentoringErrorCode.MENTORING_TIME_ERROR);
}
this.status = status;
this.topic = topic;
this.applyTimes = applyTimes;
this.mentoringMessage = mentoringMessage;
this.mentor = mentor;
this.mentee = mentee;
}

public static Mentoring of(
final MentoringStatus status,
final MentoringTopic topic,
final List<MentoringTime> applyTimes,
final String mentoringMessage,
final Mentor mentor,
final Mentee mentee
) {
return new Mentoring(
status,
topic,
applyTimes,
mentoringMessage,
mentor,
mentee
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package es.princip.ringus.domain.mentoring;


import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MentoringRepository extends JpaRepository<Mentoring, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package es.princip.ringus.domain.mentoring;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "멘토링 상태")
public enum MentoringStatus {
@Schema(description = "대기중, WAITING")
WAITING,
@Schema(description = "수락됨, ACCEPTED")
ACCEPTED,
@Schema(description = "거절됨, REJECTED")
REJECTED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package es.princip.ringus.domain.mentoring;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Getter
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MentoringTime {

private LocalDateTime startTime;
private LocalDateTime endTime;

@JsonCreator
public MentoringTime(@JsonProperty("startTime")final LocalDateTime startTime,
@JsonProperty("endTime")final LocalDateTime endTime) {
if (startTime == null || endTime == null) {
throw new IllegalArgumentException("시작 시간과 종료 시간은 필수입니다.");
}
if (!startTime.isBefore(endTime)) {
throw new IllegalArgumentException("종료 시간은 시작 시간보다 뒤에 있어야 합니다.");
}
if (startTime.isBefore(LocalDateTime.now())) {
throw new IllegalArgumentException("시작 시간이 현재 시간보다 빠를 수 없습니다.");
}
this.startTime = startTime;
this.endTime = endTime;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package es.princip.ringus.domain.mentoring;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
@Schema(description = "멘토링 주제")
public enum MentoringTopic {
@Schema(description = "학업 관련, STUDY")
STUDY("학업 관련"),
@Schema(description = "업계 동향, INDUSTRY_TRENDS")
INDUSTRY_TRENDS("업계 동향"),
@Schema(description = "면접대비, INTERVIEW")
INTERVIEW("면접 대비"),
@Schema(description = "취업 준비, JOB_PREP")
JOB_PREP("취업 준비");

private final String description;

MentoringTopic(String description) {
this.description = description;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package es.princip.ringus.domain.mentoring.converter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import es.princip.ringus.domain.mentoring.MentoringTime;
import es.princip.ringus.domain.exception.MentoringErrorCode;
import es.princip.ringus.global.exception.CustomRuntimeException;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

import java.util.ArrayList;
import java.util.List;

@Converter(autoApply = false)
public class MentoringTimeConverter implements AttributeConverter<List<MentoringTime>,String> {
private static final ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule());

@Override
public String convertToDatabaseColumn(List<MentoringTime> mentoringTimes) {
try {
if (mentoringTimes == null || mentoringTimes.isEmpty()) {
return "[]"; // 빈 리스트일 경우 빈 JSON 배열 반환
}
String json = objectMapper.writeValueAsString(mentoringTimes);
System.out.println("✅ MentoringTime JSON 변환 (DB 저장): " + json);
return json;
} catch (Exception e) {
System.err.println("❌ MentoringTime 변환 오류 (DB 저장): " + mentoringTimes);
e.printStackTrace();
throw new CustomRuntimeException(MentoringErrorCode.MENTORING_TIME_CONVERT_ERROR);
}
}

@Override
public List<MentoringTime> convertToEntityAttribute(String dbData) {
try {
if (dbData == null || dbData.isEmpty()) {
return new ArrayList<>(); // null 또는 빈 값이면 빈 리스트 반환
}
List<MentoringTime> times = objectMapper.readValue(dbData, new TypeReference<List<MentoringTime>>() {});
System.out.println("✅ MentoringTime 객체 변환 (조회 시): " + times);
return times;
} catch (JsonProcessingException e) {
System.err.println("❌ MentoringTime 변환 오류 (조회 시): " + dbData);
e.printStackTrace();
throw new CustomRuntimeException(MentoringErrorCode.MENTORING_TIME_CONVERT_ERROR);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package es.princip.ringus.presentation.mentoring;

import es.princip.ringus.application.mentoring.MentoringService;
import es.princip.ringus.global.annotation.SessionMemberId;
import es.princip.ringus.global.util.ApiResponseWrapper;
import es.princip.ringus.presentation.mentoring.dto.CreateMentoringRequest;
import es.princip.ringus.presentation.mentoring.dto.MentoringResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/mentoring")
@RequiredArgsConstructor
public class MentoringController implements MentoringControllerDocs {

private final MentoringService mentoringService;

@PostMapping
public ResponseEntity<ApiResponseWrapper<MentoringResponse>> suggest(
@SessionMemberId Long memberId,
@Valid @RequestBody CreateMentoringRequest request) {
MentoringResponse response = mentoringService.createMentoring(request, memberId);
return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "성공", response));
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package es.princip.ringus.presentation.mentoring;

import es.princip.ringus.global.annotation.SessionMemberId;
import es.princip.ringus.global.util.ApiResponseWrapper;
import es.princip.ringus.presentation.mentoring.dto.CreateMentoringRequest;
import es.princip.ringus.presentation.mentoring.dto.MentoringResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

@Tag(name = "Mentoring API", description = "멘토링 관련 API")
@RequestMapping("/mentoring")
public interface MentoringControllerDocs {

@Operation(summary = "멘토링 제안", description = "멘토링을 제안합니다.")
@ApiResponse(responseCode = "200", description = "멘토링 제안 성공")
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터")
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자")
@ApiResponse(responseCode = "404", description = "멘토 또는 멘티가 존재하지 않음")
@PostMapping
ResponseEntity<ApiResponseWrapper<MentoringResponse>> suggest(
@SessionMemberId Long memberId,
@Valid @RequestBody @Parameter CreateMentoringRequest request);
}
Loading