From ad2fba8ded15720baafb35b930e4c363ba80f904 Mon Sep 17 00:00:00 2001 From: wlgns12370 Date: Wed, 5 Mar 2025 00:48:12 +0900 Subject: [PATCH 1/2] =?UTF-8?q?RINGUS-27=20feat:=20=20=EB=A9=98=ED=86=A0?= =?UTF-8?q?=20=ED=94=84=EB=A1=9C=ED=95=84=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 ++ .../mentor/service/MentorService.java | 15 +++- .../domain/exception/SerialErrorCode.java | 31 ++++++++ .../mentor/MentorQueryDslRepository.java | 11 +++ .../mentor/MentorQueryDslRepositoryImpl.java | 61 ++++++++++++++ .../domain/mentor/MentorRepository.java | 5 +- .../domain/mentor/SerializeMentorCursor.java | 44 +++++++++++ .../princip/ringus/domain/support/Cursor.java | 18 +++++ .../ringus/domain/support/CursorDefault.java | 13 +++ .../CursorHandlerMethodArgumentResolver.java | 68 ++++++++++++++++ .../domain/support/CursorPageRequest.java | 35 ++++++++ .../ringus/domain/support/CursorPageable.java | 11 +++ .../ringus/domain/support/CursorParser.java | 33 ++++++++ .../ringus/domain/support/CursorResponse.java | 47 +++++++++++ .../domain/support/QueryDslSupport.java | 79 +++++++++++++++++++ .../common/dto/IntroductionResponse.java | 12 +++ .../common/dto/OrganizationResponse.java | 19 +++++ .../presentation/mentor/MentorController.java | 20 ++++- .../mentor/MentorSearchFilter.java | 7 ++ .../mentor/dto/MentorCardResponse.java | 37 +++++++++ 20 files changed, 564 insertions(+), 9 deletions(-) create mode 100644 src/main/java/es/princip/ringus/domain/exception/SerialErrorCode.java create mode 100644 src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java create mode 100644 src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java create mode 100644 src/main/java/es/princip/ringus/domain/mentor/SerializeMentorCursor.java create mode 100644 src/main/java/es/princip/ringus/domain/support/Cursor.java create mode 100644 src/main/java/es/princip/ringus/domain/support/CursorDefault.java create mode 100644 src/main/java/es/princip/ringus/domain/support/CursorHandlerMethodArgumentResolver.java create mode 100644 src/main/java/es/princip/ringus/domain/support/CursorPageRequest.java create mode 100644 src/main/java/es/princip/ringus/domain/support/CursorPageable.java create mode 100644 src/main/java/es/princip/ringus/domain/support/CursorParser.java create mode 100644 src/main/java/es/princip/ringus/domain/support/CursorResponse.java create mode 100644 src/main/java/es/princip/ringus/domain/support/QueryDslSupport.java create mode 100644 src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java create mode 100644 src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java create mode 100644 src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java create mode 100644 src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java diff --git a/build.gradle b/build.gradle index 4b6fe02..fd84f84 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-mysql' + implementation 'org.springframework.boot:spring-boot-starter-actuator' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' @@ -48,6 +49,12 @@ dependencies { implementation 'software.amazon.awssdk:sts' implementation 'software.amazon.awssdk:core:2.30.20' implementation 'software.amazon.awssdk:ec2' + + // QueryDSL + implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" + annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api:3.0.0" + annotationProcessor "jakarta.persistence:jakarta.persistence-api:3.0.0" } tasks.named('test') { diff --git a/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java b/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java index 466b163..099c9f8 100644 --- a/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java +++ b/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java @@ -1,15 +1,19 @@ package es.princip.ringus.application.mentor.service; import es.princip.ringus.domain.exception.MentorErrorCode; -import es.princip.ringus.domain.exception.SignUpErrorCode; -import es.princip.ringus.domain.member.Member; import es.princip.ringus.domain.member.MemberRepository; import es.princip.ringus.domain.mentor.Mentor; import es.princip.ringus.domain.mentor.MentorRepository; +import es.princip.ringus.domain.support.Cursor; +import es.princip.ringus.domain.support.CursorPageable; +import es.princip.ringus.domain.support.CursorResponse; import es.princip.ringus.global.exception.CustomRuntimeException; +import es.princip.ringus.presentation.mentor.MentorSearchFilter; import es.princip.ringus.presentation.mentor.dto.EditMentorRequest; +import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; import es.princip.ringus.presentation.mentor.dto.MentorRequest; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,6 +22,7 @@ @Transactional(readOnly = true) public class MentorService { +// private final SerializeMentorCursor serializeMentorCursor; private final MemberRepository memberRepository; private final MentorRepository mentorRepository; @@ -34,4 +39,10 @@ public Long edit(EditMentorRequest request) { mentor.edit(request); return mentor.getId(); } + + public CursorResponse getMentorBy(MentorSearchFilter filter, CursorPageable pageable) { + final Slice response = mentorRepository.findMentorBy(filter, pageable); + //final String cursor = serializeMentorCursor.serializeCursor(response); + return CursorResponse.of(response, null); + } } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/domain/exception/SerialErrorCode.java b/src/main/java/es/princip/ringus/domain/exception/SerialErrorCode.java new file mode 100644 index 0000000..daa4895 --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/exception/SerialErrorCode.java @@ -0,0 +1,31 @@ +package es.princip.ringus.domain.exception; + +import es.princip.ringus.global.exception.ErrorCode; +import org.springframework.http.HttpStatus; + +public enum SerialErrorCode implements ErrorCode { + CURSOR_NOT_SERIALIZABLE(HttpStatus.NOT_ACCEPTABLE, "Cursor not serializable"); + + SerialErrorCode(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(); + } +} diff --git a/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java new file mode 100644 index 0000000..112995c --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java @@ -0,0 +1,11 @@ +package es.princip.ringus.domain.mentor; + +import es.princip.ringus.domain.support.Cursor; +import es.princip.ringus.domain.support.CursorPageable; +import es.princip.ringus.presentation.mentor.MentorSearchFilter; +import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; +import org.springframework.data.domain.Slice; + +public interface MentorQueryDslRepository{ + Slice findMentorBy(MentorSearchFilter filter, CursorPageable pageable); +} diff --git a/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java new file mode 100644 index 0000000..10b4fba --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java @@ -0,0 +1,61 @@ +package es.princip.ringus.domain.mentor; + +import com.querydsl.core.Tuple; +import com.querydsl.core.types.OrderSpecifier; +import es.princip.ringus.domain.support.Cursor; +import es.princip.ringus.domain.support.CursorPageable; +import es.princip.ringus.domain.support.QueryDslSupport; +import es.princip.ringus.presentation.mentor.MentorSearchFilter; +import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static com.querydsl.core.types.Order.DESC; +import static es.princip.ringus.domain.mentor.QMentor.mentor; + +@Repository +@RequiredArgsConstructor +public class MentorQueryDslRepositoryImpl extends QueryDslSupport implements MentorQueryDslRepository{ + + private List fetchMentor( + final CursorPageable pageable + ) { + return queryFactory.select( + mentor.id, + mentor.nickname, + mentor.profileImage, + mentor.introduction, + mentor.organization, + mentor.message + ) + .from(mentor) + .orderBy(new OrderSpecifier<>(DESC, mentor.id)) + .limit(pageable.getPageSize() + 1) + .fetch(); + } + + private List fetchContent( + final CursorPageable pageable + ) { + return fetchMentor(pageable).stream() + .map(tuple -> MentorCardResponse.of( + tuple.get(mentor.id), + tuple.get(mentor.nickname), + tuple.get(mentor.profileImage), + tuple.get(mentor.introduction), + tuple.get(mentor.organization), + tuple.get(mentor.message), + 0 + )) + .toList(); + } + + @Override + public Slice findMentorBy(MentorSearchFilter filter, CursorPageable pageable) { + final List content = fetchContent(pageable); + return paginate(pageable, content); + } +} diff --git a/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java b/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java index 32e689d..b2e5442 100644 --- a/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java +++ b/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java @@ -1,9 +1,8 @@ package es.princip.ringus.domain.mentor; -import es.princip.ringus.domain.member.Member; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface MentorRepository extends JpaRepository { -} +public interface MentorRepository extends JpaRepository, MentorQueryDslRepository { +} \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/domain/mentor/SerializeMentorCursor.java b/src/main/java/es/princip/ringus/domain/mentor/SerializeMentorCursor.java new file mode 100644 index 0000000..a40b140 --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/mentor/SerializeMentorCursor.java @@ -0,0 +1,44 @@ +package es.princip.ringus.domain.mentor; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import es.princip.ringus.domain.exception.SerialErrorCode; +import es.princip.ringus.domain.support.Cursor; +import es.princip.ringus.global.exception.CustomRuntimeException; +import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Component; + +import java.util.Base64; + +@Slf4j +@Component +@RequiredArgsConstructor +public class SerializeMentorCursor { + + private final ObjectMapper objectMapper; + + public String serializeCursor(final Slice slice) { + if (!slice.hasNext()) { + return null; + } + final Long id = slice.getContent() + .stream() + .reduce((first, second) -> second) + .map(MentorCardResponse::mentorId) + .orElse(null); + if (id == null) { + return null; + } + final Cursor cursor = new Cursor(id); + try { + final String cursorJson = objectMapper.writeValueAsString(cursor); + return Base64.getEncoder().encodeToString(cursorJson.getBytes()); + } catch (final JsonProcessingException exception) { + log.debug("Failed to serialize cursor", exception); + throw new CustomRuntimeException(SerialErrorCode.CURSOR_NOT_SERIALIZABLE); + } + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/domain/support/Cursor.java b/src/main/java/es/princip/ringus/domain/support/Cursor.java new file mode 100644 index 0000000..a68dd4a --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/Cursor.java @@ -0,0 +1,18 @@ +package es.princip.ringus.domain.support; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +public class Cursor { + + private final Long id; + + @JsonCreator + public Cursor(@JsonProperty("id") final Long id) { + this.id = id; + } +} diff --git a/src/main/java/es/princip/ringus/domain/support/CursorDefault.java b/src/main/java/es/princip/ringus/domain/support/CursorDefault.java new file mode 100644 index 0000000..2976f1a --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/CursorDefault.java @@ -0,0 +1,13 @@ +package es.princip.ringus.domain.support; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface CursorDefault { + String value() default "cursor"; + Class type() default Cursor.class; +} diff --git a/src/main/java/es/princip/ringus/domain/support/CursorHandlerMethodArgumentResolver.java b/src/main/java/es/princip/ringus/domain/support/CursorHandlerMethodArgumentResolver.java new file mode 100644 index 0000000..32224b0 --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/CursorHandlerMethodArgumentResolver.java @@ -0,0 +1,68 @@ +package es.princip.ringus.domain.support; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class CursorHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + + private static final String DEFAULT_CURSOR_PARAMETER = "cursor"; + private static final Class DEFAULT_CURSOR_TYPE = Cursor.class; + private final CursorParser cursorParser; + + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return CursorPageable.class.equals(parameter.getParameterType()) && + parameter.hasParameterAnnotation(CursorDefault.class); + } + + @Override + public Object resolveArgument( + @NonNull final MethodParameter parameter, + final ModelAndViewContainer mavContainer, + @NonNull final NativeWebRequest webRequest, + final WebDataBinderFactory binderFactory + ) { + return resolveCursor(parameter, mavContainer, webRequest, binderFactory); + } + + public Cursor resolveCursor( + @NonNull MethodParameter parameter, + ModelAndViewContainer mavContainer, + @NonNull NativeWebRequest webRequest, + WebDataBinderFactory binderFactory + ) { + final Class cursorType = getCursorType(parameter); + final String cursorString = getCursorString(webRequest, parameter); + return cursorParser.parse(cursorType, cursorString); + }; + + protected Class getCursorType(final MethodParameter parameter) { + final CursorDefault annotation = parameter.getParameterAnnotation(CursorDefault.class); + if (annotation == null) { + return DEFAULT_CURSOR_TYPE; + } + return annotation.type(); + } + + protected String getCursorString(final NativeWebRequest webRequest, final MethodParameter parameter) { + String cursorString = webRequest.getParameter(DEFAULT_CURSOR_PARAMETER); + if (cursorString != null) { + return cursorString; + } + final CursorDefault annotation = parameter.getParameterAnnotation(CursorDefault.class); + cursorString = webRequest.getParameter(Optional.ofNullable(annotation) + .map(CursorDefault::value) + .orElseThrow(() -> new IllegalArgumentException("@CursorDefault is required"))); + return cursorString; + } +} diff --git a/src/main/java/es/princip/ringus/domain/support/CursorPageRequest.java b/src/main/java/es/princip/ringus/domain/support/CursorPageRequest.java new file mode 100644 index 0000000..11da848 --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/CursorPageRequest.java @@ -0,0 +1,35 @@ +package es.princip.ringus.domain.support; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +public class CursorPageRequest implements CursorPageable { + + private final Pageable pageable; + private final T cursor; + + public CursorPageRequest(final Pageable pageable, final T cursor) { + this.pageable = pageable; + this.cursor = cursor; + } + + @Override + public int getPageSize() { + return pageable.getPageSize(); + } + + @Override + public T getCursor() { + return cursor; + } + + @Override + public boolean hasCursor() { + return cursor != null; + } + + @Override + public Sort getSort() { + return pageable.getSort(); + } +} diff --git a/src/main/java/es/princip/ringus/domain/support/CursorPageable.java b/src/main/java/es/princip/ringus/domain/support/CursorPageable.java new file mode 100644 index 0000000..43e2722 --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/CursorPageable.java @@ -0,0 +1,11 @@ +package es.princip.ringus.domain.support; + +import org.springframework.data.domain.Sort; + +public interface CursorPageable { + + int getPageSize(); + boolean hasCursor(); + T getCursor(); + Sort getSort(); +} \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/domain/support/CursorParser.java b/src/main/java/es/princip/ringus/domain/support/CursorParser.java new file mode 100644 index 0000000..8d0353a --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/CursorParser.java @@ -0,0 +1,33 @@ +package es.princip.ringus.domain.support; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.Base64; + +@Component +@RequiredArgsConstructor +public class CursorParser { + + private final ObjectMapper objectMapper; + + public T parse(final Class clazz, final String cursorString) { + if (cursorString == null) { + return null; + } + try { + final T result = objectMapper.readValue(Base64.getDecoder().decode(cursorString), clazz); + if (result == null) { + return null; + } + if (result.getId() == null) { + throw new IllegalArgumentException("커서에 기본키가 포함되어 있지 않습니다."); + } + return result; + } catch (final IOException exception) { + return null; + } + } +} diff --git a/src/main/java/es/princip/ringus/domain/support/CursorResponse.java b/src/main/java/es/princip/ringus/domain/support/CursorResponse.java new file mode 100644 index 0000000..11975e4 --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/CursorResponse.java @@ -0,0 +1,47 @@ +package es.princip.ringus.domain.support; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.domain.Slice; +import org.springframework.data.domain.Sort; + +import java.util.List; + +@Getter +public class CursorResponse { + + private final List content; + private final SliceInfo sliceInfo; + + private CursorResponse(final List content, final SliceInfo sliceInfo) { + this.content = content; + this.sliceInfo = sliceInfo; + } + + public static CursorResponse of(final Slice slice, final String cursor) { + SliceInfo pageInfo = SliceInfo.builder() + .size(slice.getSize()) + .numberOfElements(slice.getNumberOfElements()) + .first(slice.isFirst()) + .last(slice.isLast()) + .empty(slice.isEmpty()) + .sort(slice.getSort()) + .cursor(cursor) + .build(); + return new CursorResponse<>(slice.getContent(), pageInfo); + } + + record SliceInfo( + int size, + int numberOfElements, + boolean first, + boolean last, + boolean empty, + String cursor, + Sort sort + ) { + @Builder + public SliceInfo { + } + } +} diff --git a/src/main/java/es/princip/ringus/domain/support/QueryDslSupport.java b/src/main/java/es/princip/ringus/domain/support/QueryDslSupport.java new file mode 100644 index 0000000..daac99a --- /dev/null +++ b/src/main/java/es/princip/ringus/domain/support/QueryDslSupport.java @@ -0,0 +1,79 @@ +package es.princip.ringus.domain.support; + +import com.querydsl.core.types.Order; +import com.querydsl.jpa.JPQLTemplates; +import com.querydsl.jpa.impl.JPAQuery; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.*; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.stereotype.Repository; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import static com.querydsl.core.types.Order.ASC; +import static com.querydsl.core.types.Order.DESC; + +/** + * QueryDsl 5.x 버전에 맞춘 QueryDsl 지원 라이브러리 + * + * @author Jihun Yu + * @see org.springframework.data.jpa.repository.support.QuerydslRepositorySupport 기반으로 한 Younghan Kim의 QueryDsl 4.x 지원 라이브러리 + * + */ +@Repository +public abstract class QueryDslSupport { + + private EntityManager entityManager; + protected JPAQueryFactory queryFactory; + + @Autowired + public void setEntityManager(final EntityManager entityManager) { + Assert.notNull(entityManager, "EntityManager must not be null!"); + this.entityManager = entityManager; + this.queryFactory = new JPAQueryFactory(JPQLTemplates.DEFAULT, entityManager); + } + + @PostConstruct + public void validate() { + Assert.notNull(entityManager, "EntityManager must not be null!"); + Assert.notNull(queryFactory, "QueryFactory must not be null!"); + } + + protected Page paginate( + final Pageable pageable, + final List content, + final Function> countQuery + ) { + final JPAQuery countResult = countQuery.apply(queryFactory); + return PageableExecutionUtils.getPage(content, pageable, countResult::fetchOne); + } + + protected boolean removeIfContentHasNext(final List content, final int size) { + if (content.size() > size) { + content.remove(size); + return true; + } + return false; + } + + protected Slice paginate( + final CursorPageable pageable, + final List content + ) { + final List mutable = new ArrayList<>(content); + final int size = pageable.getPageSize(); + boolean hasNext = removeIfContentHasNext(mutable, size); + return new SliceImpl<>(mutable, PageRequest.ofSize(size), hasNext); + } + + protected static Order convertTo(final Sort.Order order) { + return order == null ? null : order.isAscending() ? ASC : DESC; + } +} \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java b/src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java new file mode 100644 index 0000000..ddf3a98 --- /dev/null +++ b/src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java @@ -0,0 +1,12 @@ +package es.princip.ringus.presentation.common.dto; + +import es.princip.ringus.domain.mentor.vo.Introduction; + +public record IntroductionResponse( + String title, + String content +) { + public static IntroductionResponse of(final Introduction introduction) { + return new IntroductionResponse(introduction.getTitle(), introduction.getContent()); + } +} diff --git a/src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java b/src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java new file mode 100644 index 0000000..401c382 --- /dev/null +++ b/src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java @@ -0,0 +1,19 @@ +package es.princip.ringus.presentation.common.dto; + +import es.princip.ringus.domain.mentor.vo.Organization; + +public record OrganizationResponse( + String name, + String jobCategory, + String detailedJob, + int experience +) { + public static OrganizationResponse of(final Organization organization) { + return new OrganizationResponse( + organization.getName(), + organization.getJobCategory().name(), + organization.getDetailedJob().name(), + organization.getExperience() + ); + } +} diff --git a/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java b/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java index 84eda71..0fec803 100644 --- a/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java +++ b/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java @@ -1,14 +1,17 @@ package es.princip.ringus.presentation.mentor; import es.princip.ringus.application.mentor.service.MentorService; +import es.princip.ringus.domain.support.Cursor; +import es.princip.ringus.domain.support.CursorDefault; +import es.princip.ringus.domain.support.CursorPageable; +import es.princip.ringus.domain.support.CursorResponse; import es.princip.ringus.global.annotation.SessionMemberId; import es.princip.ringus.global.util.ApiResponseWrapper; -import es.princip.ringus.presentation.mentor.dto.EditMentorRequest; -import es.princip.ringus.presentation.mentor.dto.EditMentorResponse; -import es.princip.ringus.presentation.mentor.dto.MentorRequest; -import es.princip.ringus.presentation.mentor.dto.MentorResponse; +import es.princip.ringus.presentation.mentor.dto.*; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -31,4 +34,13 @@ public ResponseEntity> update(@Valid @Req EditMentorResponse response = EditMentorResponse.from(mentorService.edit(request)); return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "성공", response)); } + + @GetMapping + public ResponseEntity>> getMentors( + @ModelAttribute final MentorSearchFilter filter, + @CursorDefault @PageableDefault(sort = "mentorId", direction = Sort.Direction.DESC) final CursorPageable pageable + ) { + CursorResponse response = mentorService.getMentorBy(filter, pageable); + return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "성공", response)); + } } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java b/src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java new file mode 100644 index 0000000..ac15e2c --- /dev/null +++ b/src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java @@ -0,0 +1,7 @@ +package es.princip.ringus.presentation.mentor; + +public record MentorSearchFilter( + boolean bookmarked, + boolean commissioned +) { +} diff --git a/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java b/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java new file mode 100644 index 0000000..fd730c5 --- /dev/null +++ b/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java @@ -0,0 +1,37 @@ +package es.princip.ringus.presentation.mentor.dto; + +import es.princip.ringus.domain.mentor.vo.Introduction; +import es.princip.ringus.domain.mentor.vo.Organization; +import es.princip.ringus.infra.storage.domain.ProfileImage; +import es.princip.ringus.presentation.common.dto.IntroductionResponse; +import es.princip.ringus.presentation.common.dto.OrganizationResponse; + +public record MentorCardResponse( + Long mentorId, + String nickname, + String imgUrl, + IntroductionResponse introduction, + OrganizationResponse organization, + String message, + int mentoringCount +) { + public static MentorCardResponse of( + final Long mentorId, + final String nickname, + final ProfileImage profileImage, + final Introduction introduction, + final Organization organization, + final String message, + final int mentoringCount + ) { + return new MentorCardResponse( + mentorId, + nickname, + profileImage.getFilePath(), + IntroductionResponse.of(introduction), + OrganizationResponse.of(organization), + message, + mentoringCount + ); + } +} From 27c98fed1a3a4f3161acf37a925dd56070c4368a Mon Sep 17 00:00:00 2001 From: wlgns12370 Date: Thu, 13 Mar 2025 18:16:49 +0900 Subject: [PATCH 2/2] =?UTF-8?q?RINGUS-27=20feat:=20=EB=A9=98=ED=86=A0=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberMentorProfile.java | 10 +++ .../member/service/MemberService.java | 29 +++--- .../mentor/service/MentorService.java | 21 +++-- .../application/support/CursorParser.java | 18 ++++ .../princip/ringus/domain/member/Member.java | 4 +- .../ringus/domain/member/MemberProfile.java | 5 -- .../ringus/domain/member/MemberType.java | 2 +- .../domain/mentee/MenteeRepository.java | 4 + .../princip/ringus/domain/mentor/Mentor.java | 3 +- .../mentor/MentorQueryDslRepository.java | 4 +- .../mentor/MentorQueryDslRepositoryImpl.java | 26 +++--- .../domain/mentor/MentorRepository.java | 6 ++ .../ringus/domain/mentor/vo/Introduction.java | 4 +- .../ringus/domain/support/CursorResponse.java | 11 ++- .../common/dto/IntroductionResponse.java | 2 +- .../common/dto/OrganizationResponse.java | 2 +- .../common/dto/PortfolioResponse.java | 2 +- .../common/dto/TimezoneResponse.java | 2 +- .../member/dto/MemberResponse.java | 41 ++++++++- .../presentation/mentor/MentorController.java | 23 ++++- .../mentor/MentorControllerDocs.java | 17 +++- .../mentor/MentorSearchFilter.java | 7 -- .../mentor/dto/CursorRequest.java | 8 ++ .../mentor/dto/EditMentorRequest.java | 8 +- .../mentor/dto/MentorCardResponse.java | 4 +- .../mentor/dto/MentorDetailResponse.java | 8 +- .../mentor/dto/MyMentorResponse.java | 8 +- .../mentoring/MentoringController.java | 2 - src/main/resources/db/data/cursor-test.sql | 89 +++++++++++++++++++ src/main/resources/db/migration/V1__init.sql | 8 +- 30 files changed, 283 insertions(+), 95 deletions(-) create mode 100644 src/main/java/es/princip/ringus/application/member/service/MemberMentorProfile.java create mode 100644 src/main/java/es/princip/ringus/application/support/CursorParser.java delete mode 100644 src/main/java/es/princip/ringus/domain/member/MemberProfile.java delete mode 100644 src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java create mode 100644 src/main/java/es/princip/ringus/presentation/mentor/dto/CursorRequest.java create mode 100644 src/main/resources/db/data/cursor-test.sql diff --git a/src/main/java/es/princip/ringus/application/member/service/MemberMentorProfile.java b/src/main/java/es/princip/ringus/application/member/service/MemberMentorProfile.java new file mode 100644 index 0000000..170945e --- /dev/null +++ b/src/main/java/es/princip/ringus/application/member/service/MemberMentorProfile.java @@ -0,0 +1,10 @@ +package es.princip.ringus.application.member.service; + +import es.princip.ringus.domain.mentor.vo.Organization; +import es.princip.ringus.infra.storage.domain.ProfileImage; + +public record MemberMentorProfile( + ProfileImage profileImage, + Organization organization +) { +} diff --git a/src/main/java/es/princip/ringus/application/member/service/MemberService.java b/src/main/java/es/princip/ringus/application/member/service/MemberService.java index 28fc8e0..6f0f11b 100644 --- a/src/main/java/es/princip/ringus/application/member/service/MemberService.java +++ b/src/main/java/es/princip/ringus/application/member/service/MemberService.java @@ -1,5 +1,6 @@ package es.princip.ringus.application.member.service; +import es.princip.ringus.domain.common.Education; import es.princip.ringus.domain.exception.MemberErrorCode; import es.princip.ringus.domain.exception.SignUpErrorCode; import es.princip.ringus.domain.member.Member; @@ -7,6 +8,7 @@ import es.princip.ringus.domain.member.MemberType; import es.princip.ringus.domain.mentee.MenteeRepository; import es.princip.ringus.domain.mentor.MentorRepository; +import es.princip.ringus.domain.mentor.vo.Organization; import es.princip.ringus.domain.serviceTerm.ServiceTermAgreement; import es.princip.ringus.global.exception.CustomRuntimeException; import es.princip.ringus.global.util.UniversityDomainUtil; @@ -49,27 +51,22 @@ public Member createMember(SignUpRequest request, Set serv return member; } - private String getMemberImageUrl(Member member) { - if (member.getMemberType().equals(MemberType.MENTEE)) { - ProfileImage img = menteeRepository.findProfileByMemberId(member.getId()); - return img != null ? img.getFilePath() : null; - } - else if (member.getMemberType().equals(MemberType.MENTOR)) { - ProfileImage img = mentorRepository.findProfileByMemberId(member.getId()); - return img != null ? img.getFilePath() : null; - } - return ""; - } - public MemberResponse getMember(Long memberId) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> new CustomRuntimeException(MemberErrorCode.MEMBER_NOT_FOUND)); - String imgUrl = ""; if (member.isProfileRegistered()) { - imgUrl = getMemberImageUrl(member); + if (member.getMemberType().equals(MemberType.ROLE_MENTEE)) { + ProfileImage img = menteeRepository.findProfileByMemberId(member.getId()); + Education education = menteeRepository.findEducationByMemberId(member.getId()); + //null check 필요 + return MemberResponse.of(member, img.getFilePath(), education); + } else if (member.getMemberType().equals(MemberType.ROLE_MENTOR)) { + ProfileImage img = mentorRepository.findProfileByMemberId(member.getId()); + Organization organization = mentorRepository.findOrganizationByMemberId(member.getId()); + return MemberResponse.of(member, img.getFilePath(), organization); + } } - - return MemberResponse.of(member, imgUrl); + return MemberResponse.of(member); } } diff --git a/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java b/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java index 99fb436..4d098f8 100644 --- a/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java +++ b/src/main/java/es/princip/ringus/application/mentor/service/MentorService.java @@ -1,5 +1,6 @@ package es.princip.ringus.application.mentor.service; +import es.princip.ringus.application.support.CursorParser; import es.princip.ringus.domain.exception.MentorErrorCode; import es.princip.ringus.domain.exception.SignUpErrorCode; import es.princip.ringus.domain.member.Member; @@ -8,16 +9,15 @@ import es.princip.ringus.domain.mentor.MentorRepository; import es.princip.ringus.domain.support.CursorResponse; import es.princip.ringus.global.exception.CustomRuntimeException; -import es.princip.ringus.presentation.mentor.MentorSearchFilter; -import es.princip.ringus.presentation.mentor.dto.EditMentorRequest; -import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; -import es.princip.ringus.presentation.mentor.dto.MentorRequest; +import es.princip.ringus.presentation.mentor.dto.*; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import static es.princip.ringus.application.support.CursorParser.parse; + @Service @RequiredArgsConstructor @Transactional(readOnly = true) @@ -53,8 +53,15 @@ public Long edit(Long memberId, EditMentorRequest request) { return mentor.getId(); } - public CursorResponse getMentorBy(MentorSearchFilter filter, Long cursor, Pageable pageable) { - final Slice response = mentorRepository.findMentorBy(filter, cursor, pageable); - return CursorResponse.of(response, null); + public CursorResponse getMentorBy(CursorRequest request, Pageable pageable) { + final Slice response = mentorRepository.findMentorBy(request, pageable); + final Long cursor = parse(request.cursor(), response); + return CursorResponse.of(response, cursor); + } + + public MentorDetailResponse getDetailBy(Long mentorId) { + Mentor mentor = mentorRepository.findById(mentorId) + .orElseThrow(() -> new CustomRuntimeException(MentorErrorCode.MENTOR_PROFILE_NOT_FOUND)); + return MentorDetailResponse.from(mentor); } } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/application/support/CursorParser.java b/src/main/java/es/princip/ringus/application/support/CursorParser.java new file mode 100644 index 0000000..e254dd4 --- /dev/null +++ b/src/main/java/es/princip/ringus/application/support/CursorParser.java @@ -0,0 +1,18 @@ +package es.princip.ringus.application.support; + +import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; +import org.springframework.data.domain.Slice; + +public class CursorParser { + public static Long parse(final Long cursor, final Slice slice) { + if (slice.isLast()) { + return null; + } + if (slice.hasNext()) { + return slice.getContent() + .get(slice.getSize() -1) + .mentorId(); + } + return null; + } +} diff --git a/src/main/java/es/princip/ringus/domain/member/Member.java b/src/main/java/es/princip/ringus/domain/member/Member.java index ea12232..c4288a0 100644 --- a/src/main/java/es/princip/ringus/domain/member/Member.java +++ b/src/main/java/es/princip/ringus/domain/member/Member.java @@ -87,10 +87,10 @@ public void confirmUniversity() { } public boolean isNotMentor() { - return this.memberType != MemberType.MENTOR; + return this.memberType != MemberType.ROLE_MENTOR; } public boolean isNotMentee() { - return this.memberType != MemberType.MENTEE; + return this.memberType != MemberType.ROLE_MENTEE; } } diff --git a/src/main/java/es/princip/ringus/domain/member/MemberProfile.java b/src/main/java/es/princip/ringus/domain/member/MemberProfile.java deleted file mode 100644 index 555873d..0000000 --- a/src/main/java/es/princip/ringus/domain/member/MemberProfile.java +++ /dev/null @@ -1,5 +0,0 @@ -package es.princip.ringus.domain.member; - -public class MemberProfile { - -} diff --git a/src/main/java/es/princip/ringus/domain/member/MemberType.java b/src/main/java/es/princip/ringus/domain/member/MemberType.java index 2b87a70..870b301 100644 --- a/src/main/java/es/princip/ringus/domain/member/MemberType.java +++ b/src/main/java/es/princip/ringus/domain/member/MemberType.java @@ -2,5 +2,5 @@ public enum MemberType { - ADMIN, MENTOR, MENTEE + ROLE_ADMIN, ROLE_MENTOR, ROLE_MENTEE } diff --git a/src/main/java/es/princip/ringus/domain/mentee/MenteeRepository.java b/src/main/java/es/princip/ringus/domain/mentee/MenteeRepository.java index a5eccd7..82167db 100644 --- a/src/main/java/es/princip/ringus/domain/mentee/MenteeRepository.java +++ b/src/main/java/es/princip/ringus/domain/mentee/MenteeRepository.java @@ -1,5 +1,6 @@ package es.princip.ringus.domain.mentee; +import es.princip.ringus.domain.common.Education; import es.princip.ringus.infra.storage.domain.ProfileImage; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -14,4 +15,7 @@ public interface MenteeRepository extends JpaRepository { @Query("SELECT m.profileImage FROM Mentee m WHERE m.memberId = :memberId") ProfileImage findProfileByMemberId(Long memberId); + + @Query("SELECT m.education FROM Mentee m WHERE m.memberId = :memberId") + Education findEducationByMemberId(Long memberId); } diff --git a/src/main/java/es/princip/ringus/domain/mentor/Mentor.java b/src/main/java/es/princip/ringus/domain/mentor/Mentor.java index 02b06de..df9a861 100644 --- a/src/main/java/es/princip/ringus/domain/mentor/Mentor.java +++ b/src/main/java/es/princip/ringus/domain/mentor/Mentor.java @@ -63,7 +63,7 @@ public class Mentor { private List mentorings = new ArrayList<>(); // 멘티에게 전하는 말 - @Column(name = "message", length = 50) + @Column(name = "message", length = 100) private String message; // 포트폴리오 @@ -113,6 +113,7 @@ public void edit(final EditMentorRequest request) { this.hashtags = request.hashtags().stream().map(Hashtag::new).toList(); this.message = request.message(); this.portfolio = request.portfolio().toEntity(); + this.profileImage = request.image().toEntity(); } public void addMentoring(Mentoring mentoring){ diff --git a/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java index f6667ea..a7c6b30 100644 --- a/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java +++ b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepository.java @@ -1,10 +1,10 @@ package es.princip.ringus.domain.mentor; -import es.princip.ringus.presentation.mentor.MentorSearchFilter; +import es.princip.ringus.presentation.mentor.dto.CursorRequest; import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; public interface MentorQueryDslRepository{ - Slice findMentorBy(MentorSearchFilter filter, Long cursor, Pageable pageable); + Slice findMentorBy(CursorRequest request, Pageable pageable); } diff --git a/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java index fa2a824..52e5e76 100644 --- a/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java +++ b/src/main/java/es/princip/ringus/domain/mentor/MentorQueryDslRepositoryImpl.java @@ -3,7 +3,7 @@ import com.querydsl.core.Tuple; import com.querydsl.core.types.OrderSpecifier; import es.princip.ringus.domain.support.QueryDslSupport; -import es.princip.ringus.presentation.mentor.MentorSearchFilter; +import es.princip.ringus.presentation.mentor.dto.CursorRequest; import es.princip.ringus.presentation.mentor.dto.MentorCardResponse; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; @@ -12,7 +12,7 @@ import java.util.List; -import static com.querydsl.core.types.Order.DESC; +import static com.querydsl.core.types.Order.ASC; import static es.princip.ringus.domain.mentor.QMentor.mentor; @Repository @@ -32,8 +32,8 @@ private List fetchMentor( mentor.message ) .from(mentor) - .where(cursor != null ? mentor.id.lt(cursor) : null) - .orderBy(new OrderSpecifier<>(DESC, mentor.id)) + .where(cursor != null ? mentor.id.goe( cursor) : mentor.id.isNotNull()) + .orderBy(new OrderSpecifier<>(ASC, mentor.id)) .limit(pageable.getPageSize() + 1) .fetch(); } @@ -44,20 +44,20 @@ private List fetchContent( ) { return fetchMentor(pageable, cursor).stream() .map(tuple -> MentorCardResponse.of( - tuple.get(mentor.id), - tuple.get(mentor.nickname), - tuple.get(mentor.profileImage), - tuple.get(mentor.introduction), - tuple.get(mentor.organization), - tuple.get(mentor.message), - 0 + tuple.get(mentor.id), + tuple.get(mentor.nickname), + tuple.get(mentor.profileImage), + tuple.get(mentor.introduction), + tuple.get(mentor.organization), + tuple.get(mentor.message), + 0 )) .toList(); } @Override - public Slice findMentorBy(MentorSearchFilter filter, Long cursor, Pageable pageable) { - final List content = fetchContent(pageable, cursor); + public Slice findMentorBy(CursorRequest request, Pageable pageable) { + final List content = fetchContent(pageable, request.cursor()); return paginate(pageable, content); } } diff --git a/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java b/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java index 7007dbf..d35535b 100644 --- a/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java +++ b/src/main/java/es/princip/ringus/domain/mentor/MentorRepository.java @@ -1,14 +1,20 @@ package es.princip.ringus.domain.mentor; +import es.princip.ringus.domain.mentor.vo.Organization; import es.princip.ringus.infra.storage.domain.ProfileImage; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository public interface MentorRepository extends JpaRepository, MentorQueryDslRepository { boolean existsByMemberId(Long memberId); @Query("SELECT m.profileImage FROM Mentor m WHERE m.memberId = :memberId") ProfileImage findProfileByMemberId(Long memberId); + + @Query("SELECT m.organization FROM Mentor m WHERE m.memberId = :memberId") + Organization findOrganizationByMemberId(Long memberId); } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/domain/mentor/vo/Introduction.java b/src/main/java/es/princip/ringus/domain/mentor/vo/Introduction.java index 8d4b97b..6c510ab 100644 --- a/src/main/java/es/princip/ringus/domain/mentor/vo/Introduction.java +++ b/src/main/java/es/princip/ringus/domain/mentor/vo/Introduction.java @@ -14,9 +14,9 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Introduction { // 제목 - @Column(name="introduction_title", length = 15) + @Column(name="introduction_title", length = 30) private String title; // 내용 - @Column(name="introduction_content", length = 300) + @Column(name="introduction_content", length = 500) private String content; } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/domain/support/CursorResponse.java b/src/main/java/es/princip/ringus/domain/support/CursorResponse.java index 11975e4..049036d 100644 --- a/src/main/java/es/princip/ringus/domain/support/CursorResponse.java +++ b/src/main/java/es/princip/ringus/domain/support/CursorResponse.java @@ -1,5 +1,6 @@ package es.princip.ringus.domain.support; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Builder; import lombok.Getter; import org.springframework.data.domain.Slice; @@ -7,6 +8,8 @@ import java.util.List; +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + @Getter public class CursorResponse { @@ -18,14 +21,12 @@ private CursorResponse(final List content, final SliceInfo sliceInfo) { this.sliceInfo = sliceInfo; } - public static CursorResponse of(final Slice slice, final String cursor) { + public static CursorResponse of(final Slice slice, final Long cursor) { SliceInfo pageInfo = SliceInfo.builder() .size(slice.getSize()) .numberOfElements(slice.getNumberOfElements()) - .first(slice.isFirst()) .last(slice.isLast()) .empty(slice.isEmpty()) - .sort(slice.getSort()) .cursor(cursor) .build(); return new CursorResponse<>(slice.getContent(), pageInfo); @@ -34,11 +35,9 @@ public static CursorResponse of(final Slice slice, final String cursor record SliceInfo( int size, int numberOfElements, - boolean first, boolean last, boolean empty, - String cursor, - Sort sort + @JsonInclude(NON_NULL) Long cursor ) { @Builder public SliceInfo { diff --git a/src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java b/src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java index ddf3a98..80f88b7 100644 --- a/src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java +++ b/src/main/java/es/princip/ringus/presentation/common/dto/IntroductionResponse.java @@ -6,7 +6,7 @@ public record IntroductionResponse( String title, String content ) { - public static IntroductionResponse of(final Introduction introduction) { + public static IntroductionResponse from(final Introduction introduction) { return new IntroductionResponse(introduction.getTitle(), introduction.getContent()); } } diff --git a/src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java b/src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java index 401c382..0232cf4 100644 --- a/src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java +++ b/src/main/java/es/princip/ringus/presentation/common/dto/OrganizationResponse.java @@ -8,7 +8,7 @@ public record OrganizationResponse( String detailedJob, int experience ) { - public static OrganizationResponse of(final Organization organization) { + public static OrganizationResponse from(final Organization organization) { return new OrganizationResponse( organization.getName(), organization.getJobCategory().name(), diff --git a/src/main/java/es/princip/ringus/presentation/common/dto/PortfolioResponse.java b/src/main/java/es/princip/ringus/presentation/common/dto/PortfolioResponse.java index fa4215b..2c20226 100644 --- a/src/main/java/es/princip/ringus/presentation/common/dto/PortfolioResponse.java +++ b/src/main/java/es/princip/ringus/presentation/common/dto/PortfolioResponse.java @@ -6,7 +6,7 @@ public record PortfolioResponse( String url, String description ) { - public static PortfolioResponse of(final Portfolio portfolio) { + public static PortfolioResponse from(final Portfolio portfolio) { return new PortfolioResponse(portfolio.getUrl(), portfolio.getDescription()); } } diff --git a/src/main/java/es/princip/ringus/presentation/common/dto/TimezoneResponse.java b/src/main/java/es/princip/ringus/presentation/common/dto/TimezoneResponse.java index a177e9e..3ef2943 100644 --- a/src/main/java/es/princip/ringus/presentation/common/dto/TimezoneResponse.java +++ b/src/main/java/es/princip/ringus/presentation/common/dto/TimezoneResponse.java @@ -10,7 +10,7 @@ public record TimezoneResponse( LocalTime startTime, LocalTime endTime ) { - public static TimezoneResponse of(final Timezone timezone) { + public static TimezoneResponse from(final Timezone timezone) { return new TimezoneResponse( timezone.getDays(), timezone.getStartTime(), diff --git a/src/main/java/es/princip/ringus/presentation/member/dto/MemberResponse.java b/src/main/java/es/princip/ringus/presentation/member/dto/MemberResponse.java index 5a52d85..0fcc2ac 100644 --- a/src/main/java/es/princip/ringus/presentation/member/dto/MemberResponse.java +++ b/src/main/java/es/princip/ringus/presentation/member/dto/MemberResponse.java @@ -1,7 +1,14 @@ package es.princip.ringus.presentation.member.dto; +import com.fasterxml.jackson.annotation.JsonInclude; +import es.princip.ringus.domain.common.Education; import es.princip.ringus.domain.member.Member; import es.princip.ringus.domain.member.MemberType; +import es.princip.ringus.domain.mentor.vo.Organization; +import es.princip.ringus.presentation.common.dto.EducationResponse; +import es.princip.ringus.presentation.common.dto.OrganizationResponse; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; public record MemberResponse( Long memberId, @@ -9,16 +16,44 @@ public record MemberResponse( boolean isFileVerified, boolean isProfileRegisterd, boolean isUniversityVerified, - String imgUrl + @JsonInclude(NON_NULL) String imgUrl, + @JsonInclude(NON_NULL) EducationResponse education, + @JsonInclude(NON_NULL) OrganizationResponse organization ) { - public static MemberResponse of(Member member, String imgUrl) { + public static MemberResponse of(Member member) { + return new MemberResponse( + member.getId(), + member.getMemberType(), + member.isProfileRegistered(), + member.isProfileRegistered(), + member.isUniversityVerified(), + null, + null, + null + ); + } + public static MemberResponse of(Member member, String imgUrl, Education education) { + return new MemberResponse( + member.getId(), + member.getMemberType(), + member.isProfileRegistered(), + member.isProfileRegistered(), + member.isUniversityVerified(), + imgUrl, + EducationResponse.from(education), + null + ); + } + public static MemberResponse of(Member member, String imgUrl, Organization organization) { return new MemberResponse( member.getId(), member.getMemberType(), member.isProfileRegistered(), member.isProfileRegistered(), member.isUniversityVerified(), - imgUrl + imgUrl, + null, + OrganizationResponse.from(organization) ); } } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java b/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java index f1a17f3..57cbdfe 100644 --- a/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java +++ b/src/main/java/es/princip/ringus/presentation/mentor/MentorController.java @@ -1,16 +1,22 @@ package es.princip.ringus.presentation.mentor; import es.princip.ringus.application.mentor.service.MentorService; +import es.princip.ringus.domain.support.CursorResponse; import es.princip.ringus.global.annotation.SessionCheck; import es.princip.ringus.global.annotation.SessionMemberId; import es.princip.ringus.global.util.ApiResponseWrapper; import es.princip.ringus.presentation.mentor.dto.*; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +@Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/mentor") @@ -38,12 +44,23 @@ public ResponseEntity> update( } @Override + @GetMapping public ResponseEntity>> getMentors( - @ModelAttribute final MentorSearchFilter filter, - @RequestParam("cursorId") final Long cursor, + @ModelAttribute final CursorRequest request, @PageableDefault(sort = "mentorId", direction = Sort.Direction.DESC) final Pageable pageable ) { - CursorResponse response = mentorService.getMentorBy(filter, cursor, pageable); + log.info(request.toString()); + log.info(pageable.toString()); + CursorResponse response = mentorService.getMentorBy(request, pageable); + return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "성공", response)); + } + + @Override + @GetMapping("/{mentorId}") + public ResponseEntity> getMentorByMentorId( + @PathVariable Long mentorId + ) { + MentorDetailResponse response = mentorService.getDetailBy(mentorId); return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "성공", response)); } } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/presentation/mentor/MentorControllerDocs.java b/src/main/java/es/princip/ringus/presentation/mentor/MentorControllerDocs.java index cd85416..72588fd 100644 --- a/src/main/java/es/princip/ringus/presentation/mentor/MentorControllerDocs.java +++ b/src/main/java/es/princip/ringus/presentation/mentor/MentorControllerDocs.java @@ -40,9 +40,22 @@ ResponseEntity> update( @Valid @RequestBody @Parameter(description = "멘토 수정 요청") EditMentorRequest request ); + @Operation(summary = "멘토 목록 조회", description = "멘토 목록을 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "멘토 목록 조회 성공") + }) + @GetMapping ResponseEntity>> getMentors( - MentorSearchFilter filter, - Long cursor, + CursorRequest request, Pageable pageable ); + + @Operation(summary = "멘토 상세 조회", description = "멘토 상세 정보를 조회합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "멘토 상세 정보 조회 성공") + }) + @GetMapping("/{mentorId}") + ResponseEntity> getMentorByMentorId( + final Long mentorId + ); } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java b/src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java deleted file mode 100644 index 2fd6afb..0000000 --- a/src/main/java/es/princip/ringus/presentation/mentor/MentorSearchFilter.java +++ /dev/null @@ -1,7 +0,0 @@ -package es.princip.ringus.presentation.mentor; - -public record MentorSearchFilter( - boolean bookmarked, - boolean commissioned -) { -} \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/presentation/mentor/dto/CursorRequest.java b/src/main/java/es/princip/ringus/presentation/mentor/dto/CursorRequest.java new file mode 100644 index 0000000..71c12a2 --- /dev/null +++ b/src/main/java/es/princip/ringus/presentation/mentor/dto/CursorRequest.java @@ -0,0 +1,8 @@ +package es.princip.ringus.presentation.mentor.dto; + +public record CursorRequest( + boolean bookmarked, + boolean commissioned, + Long cursor +) { +} diff --git a/src/main/java/es/princip/ringus/presentation/mentor/dto/EditMentorRequest.java b/src/main/java/es/princip/ringus/presentation/mentor/dto/EditMentorRequest.java index 207a1f7..44ff616 100644 --- a/src/main/java/es/princip/ringus/presentation/mentor/dto/EditMentorRequest.java +++ b/src/main/java/es/princip/ringus/presentation/mentor/dto/EditMentorRequest.java @@ -1,9 +1,6 @@ package es.princip.ringus.presentation.mentor.dto; -import es.princip.ringus.presentation.common.dto.EducationRequest; -import es.princip.ringus.presentation.common.dto.IntroductionRequest; -import es.princip.ringus.presentation.common.dto.OrganizationRequest; -import es.princip.ringus.presentation.common.dto.PortfolioRequest; +import es.princip.ringus.presentation.common.dto.*; import jakarta.validation.constraints.NotBlank; import java.util.List; @@ -18,6 +15,7 @@ public record EditMentorRequest( TimezoneRequest timezone, List hashtags, String message, - PortfolioRequest portfolio + PortfolioRequest portfolio, + ProfileImageRequest image ) { } \ No newline at end of file diff --git a/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java b/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java index fd730c5..28f0d6c 100644 --- a/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java +++ b/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorCardResponse.java @@ -28,8 +28,8 @@ public static MentorCardResponse of( mentorId, nickname, profileImage.getFilePath(), - IntroductionResponse.of(introduction), - OrganizationResponse.of(organization), + IntroductionResponse.from(introduction), + OrganizationResponse.from(organization), message, mentoringCount ); diff --git a/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorDetailResponse.java b/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorDetailResponse.java index 26e684b..ec84d1c 100644 --- a/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorDetailResponse.java +++ b/src/main/java/es/princip/ringus/presentation/mentor/dto/MentorDetailResponse.java @@ -22,13 +22,13 @@ public static MentorDetailResponse from(final Mentor mentor) { return new MentorDetailResponse( mentor.getNickname(), EducationResponse.from(mentor.getEducation()), - OrganizationResponse.of(mentor.getOrganization()), - IntroductionResponse.of(mentor.getIntroduction()), - TimezoneResponse.of(mentor.getTimezone()), + OrganizationResponse.from(mentor.getOrganization()), + IntroductionResponse.from(mentor.getIntroduction()), + TimezoneResponse.from(mentor.getTimezone()), mentor.getMentoringField().stream().map(String::valueOf).collect(Collectors.toSet()), mentor.getHashtags().stream().map(String::valueOf).toList(), mentor.getMessage(), - PortfolioResponse.of(mentor.getPortfolio()) + PortfolioResponse.from(mentor.getPortfolio()) ); } } diff --git a/src/main/java/es/princip/ringus/presentation/mentor/dto/MyMentorResponse.java b/src/main/java/es/princip/ringus/presentation/mentor/dto/MyMentorResponse.java index ddb9af9..dd994f4 100644 --- a/src/main/java/es/princip/ringus/presentation/mentor/dto/MyMentorResponse.java +++ b/src/main/java/es/princip/ringus/presentation/mentor/dto/MyMentorResponse.java @@ -22,13 +22,13 @@ public static MyMentorResponse from(final Mentor mentor) { return new MyMentorResponse( mentor.getNickname(), EducationResponse.from(mentor.getEducation()), - OrganizationResponse.of(mentor.getOrganization()), - IntroductionResponse.of(mentor.getIntroduction()), - TimezoneResponse.of(mentor.getTimezone()), + OrganizationResponse.from(mentor.getOrganization()), + IntroductionResponse.from(mentor.getIntroduction()), + TimezoneResponse.from(mentor.getTimezone()), mentor.getMentoringField().stream().map(String::valueOf).collect(Collectors.toSet()), mentor.getHashtags().stream().map(String::valueOf).toList(), mentor.getMessage(), - PortfolioResponse.of(mentor.getPortfolio()) + PortfolioResponse.from(mentor.getPortfolio()) ); } } diff --git a/src/main/java/es/princip/ringus/presentation/mentoring/MentoringController.java b/src/main/java/es/princip/ringus/presentation/mentoring/MentoringController.java index 221de85..6c33cc4 100644 --- a/src/main/java/es/princip/ringus/presentation/mentoring/MentoringController.java +++ b/src/main/java/es/princip/ringus/presentation/mentoring/MentoringController.java @@ -30,6 +30,4 @@ public ResponseEntity> suggest( MentoringResponse response = mentoringService.createMentoring(request, memberId); return ResponseEntity.ok(ApiResponseWrapper.success(HttpStatus.OK, "성공", response)); } - - } diff --git a/src/main/resources/db/data/cursor-test.sql b/src/main/resources/db/data/cursor-test.sql new file mode 100644 index 0000000..28e7674 --- /dev/null +++ b/src/main/resources/db/data/cursor-test.sql @@ -0,0 +1,89 @@ +INSERT INTO member (email, password, member_type, is_file_verified, is_profile_registered, is_university_verified) VALUES + ('user1@example.com', 'password1', 'ROLE_MENTOR', false, false, false), + ('user2@example.com', 'password2', 'ROLE_MENTOR', false, false, false), + ('user3@example.com', 'password3', 'ROLE_MENTOR', false, false, false), + ('user4@example.com', 'password4', 'ROLE_MENTOR', false, false, false), + ('user5@example.com', 'password5', 'ROLE_MENTOR', false, false, false), + ('user6@example.com', 'password6', 'ROLE_MENTOR', false, false, false), + ('user7@example.com', 'password7', 'ROLE_MENTOR', false, false, false), + ('user8@example.com', 'password8', 'ROLE_MENTOR', false, false, false), + ('user9@example.com', 'password9', 'ROLE_MENTOR', false, false, false), + ('user10@example.com', 'password10', 'ROLE_MENTOR', false, false, false), + ('user11@example.com', 'password11', 'ROLE_MENTOR', false, false, false), + ('user12@example.com', 'password12', 'ROLE_MENTOR', false, false, false), + ('user13@example.com', 'password13', 'ROLE_MENTOR', false, false, false), + ('user14@example.com', 'password14', 'ROLE_MENTOR', false, false, false), + ('user15@example.com', 'password15', 'ROLE_MENTOR', false, false, false), + ('user16@example.com', 'password16', 'ROLE_MENTOR', false, false, false), + ('user17@example.com', 'password17', 'ROLE_MENTOR', false, false, false), + ('user18@example.com', 'password18', 'ROLE_MENTOR', false, false, false), + ('user19@example.com', 'password19', 'ROLE_MENTOR', false, false, false), + ('user20@example.com', 'password20', 'ROLE_MENTOR', false, false, false), + ('user21@example.com', 'password21', 'ROLE_MENTOR', false, false, false), + ('user22@example.com', 'password22', 'ROLE_MENTOR', false, false, false), + ('user23@example.com', 'password23', 'ROLE_MENTOR', false, false, false), + ('user24@example.com', 'password24', 'ROLE_MENTOR', false, false, false), + ('user25@example.com', 'password25', 'ROLE_MENTOR', false, false, false), + ('user26@example.com', 'password26', 'ROLE_MENTOR', false, false, false), + ('user27@example.com', 'password27', 'ROLE_MENTOR', false, false, false), + ('user28@example.com', 'password28', 'ROLE_MENTOR', false, false, false), + ('user29@example.com', 'password29', 'ROLE_MENTOR', false, false, false), + ('user30@example.com', 'password30', 'ROLE_MENTOR', false, false, false), + ('user31@example.com', 'password31', 'ROLE_MENTOR', false, false, false), + ('user32@example.com', 'password32', 'ROLE_MENTOR', false, false, false), + ('user33@example.com', 'password33', 'ROLE_MENTOR', false, false, false), + ('user34@example.com', 'password34', 'ROLE_MENTOR', false, false, false), + ('user35@example.com', 'password35', 'ROLE_MENTOR', false, false, false), + ('user36@example.com', 'password36', 'ROLE_MENTOR', false, false, false), + ('user37@example.com', 'password37', 'ROLE_MENTOR', false, false, false), + ('user38@example.com', 'password38', 'ROLE_MENTOR', false, false, false), + ('user39@example.com', 'password39', 'ROLE_MENTOR', false, false, false), + ('user40@example.com', 'password40', 'ROLE_MENTOR', false, false, false), + ('user41@example.com', 'password41', 'ROLE_MENTOR', false, false, false), + ('user42@example.com', 'password42', 'ROLE_MENTOR', false, false, false), + ('user43@example.com', 'password43', 'ROLE_MENTOR', false, false, false); + +INSERT INTO mentor (end_time, experience, start_time, member_id, introduction_title, message, introduction_content, description, file_name, file_path, major, name, nickname, school_name, url, days, detailed_job, job_category) VALUES + ('18:00:00', 5, '09:00:00', 1, 'Intro1', 'Message1', 'Content1', 'Description1', 'file1.jpg', '/path/to/file1', 'Major1', 'Name1', 'Nickname1', 'School1', 'http://url1.com', 'Mon-Fri', 'BACKEND', 'DEVELOPMENT'), + ('18:00:00', 5, '09:00:00', 2, 'Intro2', 'Message2', 'Content2', 'Description2', 'file2.jpg', '/path/to/file2', 'Major2', 'Name2', 'Nickname2', 'School2', 'http://url2.com', 'Mon-Fri', 'FRONTEND', 'DEVELOPMENT'), + ('18:00:00', 5, '09:00:00', 3, 'Intro3', 'Message3', 'Content3', 'Description3', 'file3.jpg', '/path/to/file3', 'Major3', 'Name3', 'Nickname3', 'School3', 'http://url3.com', 'Mon-Fri', 'DATA_SCIENTIST', 'DATA'), + ('18:00:00', 5, '09:00:00', 4, 'Intro4', 'Message4', 'Content4', 'Description4', 'file4.jpg', '/path/to/file4', 'Major4', 'Name4', 'Nickname4', 'School4', 'http://url4.com', 'Mon-Fri', 'UX_UI_DESIGN', 'DESIGN'), + ('18:00:00', 5, '09:00:00', 5, 'Intro5', 'Message5', 'Content5', 'Description5', 'file5.jpg', '/path/to/file5', 'Major5', 'Name5', 'Nickname5', 'School5', 'http://url5.com', 'Mon-Fri', 'BRAND_MARKETING', 'MARKETING'), + ('18:00:00', 5, '09:00:00', 6, 'Intro6', 'Message6', 'Content6', 'Description6', 'file6.jpg', '/path/to/file6', 'Major6', 'Name6', 'Nickname6', 'School6', 'http://url6.com', 'Mon-Fri', 'CLINICAL_DOCTOR', 'MEDICAL'), + ('18:00:00', 5, '09:00:00', 7, 'Intro7', 'Message7', 'Content7', 'Description7', 'file7.jpg', '/path/to/file7', 'Major7', 'Name7', 'Nickname7', 'School7', 'http://url7.com', 'Mon-Fri', 'LAWYER', 'LEGAL'), + ('18:00:00', 5, '09:00:00', 8, 'Intro8', 'Message8', 'Content8', 'Description8', 'file8.jpg', '/path/to/file8', 'Major8', 'Name8', 'Nickname8', 'School8', 'http://url8.com', 'Mon-Fri', 'HR_OPERATION', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 9, 'Intro9', 'Message9', 'Content9', 'Description9', 'file9.jpg', '/path/to/file9', 'Major9', 'Name9', 'Nickname9', 'School9', 'http://url9.com', 'Mon-Fri', 'CONSULTANT', 'FINANCE_CONSULTING_VC'), + ('18:00:00', 5, '09:00:00', 10, 'Intro10', 'Message10', 'Content10', 'Description10', 'file10.jpg', '/path/to/file10', 'Major10', 'Name10', 'Nickname10', 'School10', 'http://url10.com', 'Mon-Fri', 'SYSTEM_NETWORK', 'DEVELOPMENT'), + ('18:00:00', 5, '09:00:00', 11, 'Intro11', 'Message11', 'Content11', 'Description11', 'file11.jpg', '/path/to/file11', 'Major11', 'Name11', 'Nickname11', 'School11', 'http://url11.com', 'Mon-Fri', 'PRODUCT_DESIGN', 'DESIGN'), + ('18:00:00', 5, '09:00:00', 12, 'Intro12', 'Message12', 'Content12', 'Description12', 'file12.jpg', '/path/to/file12', 'Major12', 'Name12', 'Nickname12', 'School12', 'http://url12.com', 'Mon-Fri', 'RECRUITER', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 13, 'Intro13', 'Message13', 'Content13', 'Description13', 'file13.jpg', '/path/to/file13', 'Major13', 'Name13', 'Nickname13', 'School13', 'http://url13.com', 'Mon-Fri', 'DATA_ANALYST', 'DATA'), + ('18:00:00', 5, '09:00:00', 14, 'Intro14', 'Message14', 'Content14', 'Description14', 'file14.jpg', '/path/to/file14', 'Major14', 'Name14', 'Nickname14', 'School14', 'http://url14.com', 'Mon-Fri', 'COPYWRITER', 'MARKETING'), + ('18:00:00', 5, '09:00:00', 15, 'Intro15', 'Message15', 'Content15', 'Description15', 'file15.jpg', '/path/to/file15', 'Major15', 'Name15', 'Nickname15', 'School15', 'http://url15.com', 'Mon-Fri', 'CLINICAL_RESEARCHER', 'MEDICAL'), + ('18:00:00', 5, '09:00:00', 16, 'Intro16', 'Message16', 'Content16', 'Description16', 'file16.jpg', '/path/to/file16', 'Major16', 'Name16', 'Nickname16', 'School16', 'http://url16.com', 'Mon-Fri', 'LEGAL_ADVISOR', 'LEGAL'), + ('18:00:00', 5, '09:00:00', 17, 'Intro17', 'Message17', 'Content17', 'Description17', 'file17.jpg', '/path/to/file17', 'Major17', 'Name17', 'Nickname17', 'School17', 'http://url17.com', 'Mon-Fri', 'HR_PLANNING', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 18, 'Intro18', 'Message18', 'Content18', 'Description18', 'file18.jpg', '/path/to/file18', 'Major18', 'Name18', 'Nickname18', 'School18', 'http://url18.com', 'Mon-Fri', 'STRATEGY_PLANNING', 'FINANCE_CONSULTING_VC'), + ('18:00:00', 5, '09:00:00', 19, 'Intro19', 'Message19', 'Content19', 'Description19', 'file19.jpg', '/path/to/file19', 'Major19', 'Name19', 'Nickname19', 'School19', 'http://url19.com', 'Mon-Fri', 'DEVOPS', 'DEVELOPMENT'), + ('18:00:00', 5, '09:00:00', 20, 'Intro20', 'Message20', 'Content20', 'Description20', 'file20.jpg', '/path/to/file20', 'Major20', 'Name20', 'Nickname20', 'School20', 'http://url20.com', 'Mon-Fri', 'GRAPHIC_DESIGN', 'DESIGN'), + ('18:00:00', 5, '09:00:00', 21, 'Intro21', 'Message21', 'Content21', 'Description21', 'file21.jpg', '/path/to/file21', 'Major21', 'Name21', 'Nickname21', 'School21', 'http://url21.com', 'Mon-Fri', 'TALENT_DEVELOPMENT', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 22, 'Intro22', 'Message22', 'Content22', 'Description22', 'file22.jpg', '/path/to/file22', 'Major22', 'Name22', 'Nickname22', 'School22', 'http://url22.com', 'Mon-Fri', 'DATA_ARCHITECT', 'DATA'), + ('18:00:00', 5, '09:00:00', 23, 'Intro23', 'Message23', 'Content23', 'Description23', 'file23.jpg', '/path/to/file23', 'Major23', 'Name23', 'Nickname23', 'School23', 'http://url23.com', 'Mon-Fri', 'CONTENT_MARKETING', 'MARKETING'), + ('18:00:00', 5, '09:00:00', 24, 'Intro24', 'Message24', 'Content24', 'Description24', 'file24.jpg', '/path/to/file24', 'Major24', 'Name24', 'Nickname24', 'School24', 'http://url24.com', 'Mon-Fri', 'PHARMACEUTICAL_RESEARCHER', 'MEDICAL'), + ('18:00:00', 5, '09:00:00', 25, 'Intro25', 'Message25', 'Content25', 'Description25', 'file25.jpg', '/path/to/file25', 'Major25', 'Name25', 'Nickname25', 'School25', 'http://url25.com', 'Mon-Fri', 'LEGAL_COUNSEL', 'LEGAL'), + ('18:00:00', 5, '09:00:00', 26, 'Intro26', 'Message26', 'Content26', 'Description26', 'file26.jpg', '/path/to/file26', 'Major26', 'Name26', 'Nickname26', 'School26', 'http://url26.com', 'Mon-Fri', 'HR_OPERATION', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 27, 'Intro27', 'Message27', 'Content27', 'Description27', 'file27.jpg', '/path/to/file27', 'Major27', 'Name27', 'Nickname27', 'School27', 'http://url27.com', 'Mon-Fri', 'CONSULTANT', 'FINANCE_CONSULTING_VC'), + ('18:00:00', 5, '09:00:00', 28, 'Intro28', 'Message28', 'Content28', 'Description28', 'file28.jpg', '/path/to/file28', 'Major28', 'Name28', 'Nickname28', 'School28', 'http://url28.com', 'Mon-Fri', 'SYSTEM_NETWORK', 'DEVELOPMENT'), + ('18:00:00', 5, '09:00:00', 29, 'Intro29', 'Message29', 'Content29', 'Description29', 'file29.jpg', '/path/to/file29', 'Major29', 'Name29', 'Nickname29', 'School29', 'http://url29.com', 'Mon-Fri', 'PRODUCT_DESIGN', 'DESIGN'), + ('18:00:00', 5, '09:00:00', 30, 'Intro30', 'Message30', 'Content30', 'Description30', 'file30.jpg', '/path/to/file30', 'Major30', 'Name30', 'Nickname30', 'School30', 'http://url30.com', 'Mon-Fri', 'RECRUITER', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 31, 'Intro31', 'Message31', 'Content31', 'Description31', 'file31.jpg', '/path/to/file31', 'Major31', 'Name31', 'Nickname31', 'School31', 'http://url31.com', 'Mon-Fri', 'DATA_ANALYST', 'DATA'), + ('18:00:00', 5, '09:00:00', 32, 'Intro32', 'Message32', 'Content32', 'Description32', 'file32.jpg', '/path/to/file32', 'Major32', 'Name32', 'Nickname32', 'School32', 'http://url32.com', 'Mon-Fri', 'COPYWRITER', 'MARKETING'), + ('18:00:00', 5, '09:00:00', 33, 'Intro33', 'Message33', 'Content33', 'Description33', 'file33.jpg', '/path/to/file33', 'Major33', 'Name33', 'Nickname33', 'School33', 'http://url33.com', 'Mon-Fri', 'CLINICAL_RESEARCHER', 'MEDICAL'), + ('18:00:00', 5, '09:00:00', 34, 'Intro34', 'Message34', 'Content34', 'Description34', 'file34.jpg', '/path/to/file34', 'Major34', 'Name34', 'Nickname34', 'School34', 'http://url34.com', 'Mon-Fri', 'LEGAL_ADVISOR', 'LEGAL'), + ('18:00:00', 5, '09:00:00', 35, 'Intro35', 'Message35', 'Content35', 'Description35', 'file35.jpg', '/path/to/file35', 'Major35', 'Name35', 'Nickname35', 'School35', 'http://url35.com', 'Mon-Fri', 'HR_PLANNING', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 36, 'Intro36', 'Message36', 'Content36', 'Description36', 'file36.jpg', '/path/to/file36', 'Major36', 'Name36', 'Nickname36', 'School36', 'http://url36.com', 'Mon-Fri', 'STRATEGY_PLANNING', 'FINANCE_CONSULTING_VC'), + ('18:00:00', 5, '09:00:00', 37, 'Intro37', 'Message37', 'Content37', 'Description37', 'file37.jpg', '/path/to/file37', 'Major37', 'Name37', 'Nickname37', 'School37', 'http://url37.com', 'Mon-Fri', 'DEVOPS', 'DEVELOPMENT'), + ('18:00:00', 5, '09:00:00', 38, 'Intro38', 'Message38', 'Content38', 'Description38', 'file38.jpg', '/path/to/file38', 'Major38', 'Name38', 'Nickname38', 'School38', 'http://url38.com', 'Mon-Fri', 'GRAPHIC_DESIGN', 'DESIGN'), + ('18:00:00', 5, '09:00:00', 39, 'Intro39', 'Message39', 'Content39', 'Description39', 'file39.jpg', '/path/to/file39', 'Major39', 'Name39', 'Nickname39', 'School39', 'http://url39.com', 'Mon-Fri', 'TALENT_DEVELOPMENT', 'HR_SUPPORT'), + ('18:00:00', 5, '09:00:00', 40, 'Intro40', 'Message40', 'Content40', 'Description40', 'file40.jpg', '/path/to/file40', 'Major40', 'Name40', 'Nickname40', 'School40', 'http://url40.com', 'Mon-Fri', 'DATA_ARCHITECT', 'DATA'), + ('18:00:00', 5, '09:00:00', 41, 'Intro41', 'Message41', 'Content41', 'Description41', 'file41.jpg', '/path/to/file41', 'Major41', 'Name41', 'Nickname41', 'School41', 'http://url41.com', 'Mon-Fri', 'CONTENT_MARKETING', 'MARKETING'), + ('18:00:00', 5, '09:00:00', 42, 'Intro42', 'Message42', 'Content42', 'Description42', 'file42.jpg', '/path/to/file42', 'Major42', 'Name42', 'Nickname42', 'School42', 'http://url42.com', 'Mon-Fri', 'PHARMACEUTICAL_RESEARCHER', 'MEDICAL'), + ('18:00:00', 5, '09:00:00', 43, 'Intro43', 'Message43', 'Content43', 'Description43', 'file43.jpg', '/path/to/file43', 'Major43', 'Name43', 'Nickname43', 'School43', 'http://url43.com', 'Mon-Fri', 'LEGAL_COUNSEL', 'LEGAL'); diff --git a/src/main/resources/db/migration/V1__init.sql b/src/main/resources/db/migration/V1__init.sql index 082d0b7..8fd4ef1 100644 --- a/src/main/resources/db/migration/V1__init.sql +++ b/src/main/resources/db/migration/V1__init.sql @@ -26,7 +26,7 @@ CREATE TABLE member ( updated_at DATETIME(6), email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, - member_type ENUM ('ADMIN', 'MENTEE', 'MENTOR'), + member_type ENUM ('ROLE_ADMIN', 'ROLE_MENTEE', 'ROLE_MENTOR'), PRIMARY KEY (member_id) ); @@ -48,9 +48,9 @@ CREATE TABLE mentor ( start_time TIME(6), member_id BIGINT, mentor_id BIGINT NOT NULL AUTO_INCREMENT, - introduction_title VARCHAR(15), - message VARCHAR(50), - introduction_content VARCHAR(300), + introduction_title VARCHAR(30), + message VARCHAR(100), + introduction_content VARCHAR(500), description VARCHAR(255), file_name VARCHAR(255) NOT NULL, file_path VARCHAR(255) NOT NULL,