From d334a04418068ee54d906ed6650c490e0180cab6 Mon Sep 17 00:00:00 2001 From: MinJunKKang Date: Wed, 20 Aug 2025 22:42:59 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=EA=B3=84=EC=97=B4=EC=9D=84=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4(f=5Fid)=20=EC=97=B0=EA=B2=B0=EB=90=9C=20=ED=95=99?= =?UTF-8?q?=EA=B3=BC=20=EC=A0=95=EB=B3=B4=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/FieldMarkController.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/controller/FieldMarkController.java b/src/main/java/hackerthon/likelion13th/canfly/search/controller/FieldMarkController.java index 3f87cf8..4160ffb 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/search/controller/FieldMarkController.java +++ b/src/main/java/hackerthon/likelion13th/canfly/search/controller/FieldMarkController.java @@ -5,16 +5,16 @@ import hackerthon.likelion13th.canfly.global.api.SuccessCode; import hackerthon.likelion13th.canfly.login.auth.mapper.CustomUserDetails; import hackerthon.likelion13th.canfly.login.service.UserService; +import hackerthon.likelion13th.canfly.search.dto.MajorDto; import hackerthon.likelion13th.canfly.search.service.FieldService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @Tag(name = "계열 북마크 관련 컨트롤러", description = "계열 북마크 컨트롤러입니다") @RestController @@ -38,4 +38,18 @@ public ApiResponse toggleLike( return ApiResponse.onSuccess(SuccessCode.FIELD_LIKE_SUCCESS, true); } + + @Operation(summary = "계열에 포함된 전공 목록", + description = "계열(fieldId)에 속한 전공(major) 목록을 반환합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses. + ApiResponse(responseCode = "FIELD_MAJOR_2007", description = "계열 기준 전공 목록 조회 완료") + }) + @GetMapping("/majors") + public ApiResponse> getMajorsByField( + @PathVariable Long fieldId + ) { + List result = fieldService.getMajorsByField(fieldId); + return ApiResponse.onSuccess(SuccessCode.FIELD_MAJOR_LIST_BY_FIELD_SUCCESS, result); + } } From 2f3eba3854c7d65c3bce63c3ed5bde7c1c5ffece Mon Sep 17 00:00:00 2001 From: MinJunKKang Date: Wed, 20 Aug 2025 22:43:16 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EA=B3=84=EC=97=B4=EC=9D=84=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4(f=5Fid)=20=EC=97=B0=EA=B2=B0=EB=90=9C=20=ED=95=99?= =?UTF-8?q?=EA=B3=BC=20=EC=A0=95=EB=B3=B4=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20(=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4,=20=EB=A0=88=ED=8F=AC,=20=EC=84=B1=EA=B3=B5?= =?UTF-8?q?=EC=BD=94=EB=93=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../likelion13th/canfly/global/api/SuccessCode.java | 1 + .../canfly/search/repository/MajorRepository.java | 11 +++++++++++ .../canfly/search/service/FieldService.java | 13 +++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java b/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java index d0650a1..4c47f6b 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java +++ b/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java @@ -46,6 +46,7 @@ public enum SuccessCode implements BaseCode { // 성공 MAJOR_LIST_VIEW_SUCCESS(HttpStatus.OK, "MAJOR_2007", "전공 전체 조회 완료."), MAJOR_UNIV_LIKED_LIST_VIEW_SUCCESS(HttpStatus.OK, "MAJOR_UNIV_2006", "전공별 대학-전공 북마크 조회 완료"), MAJOR_UNIV_LIST_BY_MAJOR_SUCCESS(HttpStatus.OK, "MAJOR_UNIV_2007", "전공 기준 대학 목록 조회 완료"), + FIELD_MAJOR_LIST_BY_FIELD_SUCCESS(HttpStatus.OK, "FIELD_MAJOR_2007", "계열 기준 전공 목록 조회 완료"), // 내가 추가한 코드 TOKEN_PROCESS_SUCCESS(HttpStatus.OK, "TOKEN_2001", "코인(토큰) 사용이 완료되었습니다."); diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorRepository.java b/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorRepository.java index bf942a1..34c27e7 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorRepository.java +++ b/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorRepository.java @@ -2,9 +2,20 @@ import hackerthon.likelion13th.canfly.domain.major.Major; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import io.lettuce.core.dynamic.annotation.Param; +import java.util.List; import java.util.Optional; public interface MajorRepository extends JpaRepository { Optional findByCode(Integer code); // m_code == majorSeq + + @Query(value = """ + SELECT m.m_id AS id, m.m_name AS name + FROM major m + WHERE m.f_id = :fieldId + ORDER BY m.m_name + """, nativeQuery = true) + List findMajorsByFieldId(@Param("fieldId") Long fieldId); } \ No newline at end of file diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/service/FieldService.java b/src/main/java/hackerthon/likelion13th/canfly/search/service/FieldService.java index fe46a73..8eba769 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/search/service/FieldService.java +++ b/src/main/java/hackerthon/likelion13th/canfly/search/service/FieldService.java @@ -5,8 +5,10 @@ import hackerthon.likelion13th.canfly.global.api.ErrorCode; import hackerthon.likelion13th.canfly.global.exception.GeneralException; import hackerthon.likelion13th.canfly.search.dto.FieldDto; +import hackerthon.likelion13th.canfly.search.dto.MajorDto; import hackerthon.likelion13th.canfly.search.repository.FieldBookmarkRepository; import hackerthon.likelion13th.canfly.search.repository.FieldRepository; +import hackerthon.likelion13th.canfly.search.repository.MajorRepository; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -20,6 +22,7 @@ public class FieldService { private final FieldBookmarkRepository fieldBookmarkRepository; private final FieldRepository fieldRepository; + private final MajorRepository majorRepository; @Transactional public Field toggleFieldLike(Long fieldId, User user) { @@ -55,4 +58,14 @@ public List getAllFields() { .map(f -> new FieldDto(f.getId(), f.getName())) .toList(); } + + public List getMajorsByField(Long fieldId) { + List rows = majorRepository.findMajorsByFieldId(fieldId); + return rows.stream() + .map(r -> new MajorDto( + ((Number) r[0]).longValue(), // m_id + (String) r[1] // m_name + )) + .toList(); + } } From eee353a62598f9e4e43e597b20f44623f3e8d0ec Mon Sep 17 00:00:00 2001 From: MinJunKKang Date: Wed, 20 Aug 2025 23:15:19 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EC=A0=84=EA=B3=B5+=EB=8C=80=ED=95=99=20?= =?UTF-8?q?=EB=B6=81=EB=A7=88=ED=81=AC=20=ED=95=9C=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/controller/MajorController.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/controller/MajorController.java b/src/main/java/hackerthon/likelion13th/canfly/search/controller/MajorController.java index 2728102..93e3fe1 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/search/controller/MajorController.java +++ b/src/main/java/hackerthon/likelion13th/canfly/search/controller/MajorController.java @@ -6,6 +6,7 @@ import hackerthon.likelion13th.canfly.login.auth.mapper.CustomUserDetails; import hackerthon.likelion13th.canfly.login.service.UserService; import hackerthon.likelion13th.canfly.search.dto.MajorDto; +import hackerthon.likelion13th.canfly.search.dto.UnivMajorTupleDto; import hackerthon.likelion13th.canfly.search.service.MajorService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -54,4 +55,19 @@ public ApiResponse> getMajors( return ApiResponse.onSuccess(SuccessCode.MAJOR_LIST_VIEW_SUCCESS, majors); } + + @Operation(summary = "전공+대학 북마크 전체", + description = "로그인 사용자의 전공+대학 북마크 목록을 반환합니다. (univ_id IS NOT NULL)") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses. + ApiResponse(responseCode = "MAJOR_UNIV_BOOKMARK_2008", description = "전공+대학 북마크 목록 조회 완료") + }) + @GetMapping("/univ/list") + public ApiResponse> getLikedMajorUnivList( + @AuthenticationPrincipal CustomUserDetails customUserDetails + ) { + User user = userService.findUserByProviderId(customUserDetails.getUsername()); + List result = majorService.getAllBookmarkedMajorUnivPairs(user); + return ApiResponse.onSuccess(SuccessCode.MAJOR_UNIV_BOOKMARK_LIST_SUCCESS, result); + } } From 1dcaae1c03c67604d0c6ea7df877620413fa3b05 Mon Sep 17 00:00:00 2001 From: MinJunKKang Date: Wed, 20 Aug 2025 23:16:02 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=EC=A0=84=EA=B3=B5+=EB=8C=80=ED=95=99=20?= =?UTF-8?q?=EB=B6=81=EB=A7=88=ED=81=AC=20=ED=95=9C=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20(?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EB=A0=88?= =?UTF-8?q?=ED=8F=AC,=20=EC=84=9C=EB=B9=84=EC=8A=A4,=20=EC=84=B1=EA=B3=B5?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C,=20=EB=94=94=ED=8B=B0=EC=98=A4=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../canfly/global/api/SuccessCode.java | 1 + .../canfly/search/dto/UnivMajorTupleDto.java | 17 ++++++++++++++++ .../repository/FieldBookmarkRepository.java | 2 +- .../repository/MajorBookmarkRepository.java | 20 ++++++++++++++++++- .../canfly/search/service/MajorService.java | 18 +++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/main/java/hackerthon/likelion13th/canfly/search/dto/UnivMajorTupleDto.java diff --git a/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java b/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java index 4c47f6b..dee7e53 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java +++ b/src/main/java/hackerthon/likelion13th/canfly/global/api/SuccessCode.java @@ -47,6 +47,7 @@ public enum SuccessCode implements BaseCode { // 성공 MAJOR_UNIV_LIKED_LIST_VIEW_SUCCESS(HttpStatus.OK, "MAJOR_UNIV_2006", "전공별 대학-전공 북마크 조회 완료"), MAJOR_UNIV_LIST_BY_MAJOR_SUCCESS(HttpStatus.OK, "MAJOR_UNIV_2007", "전공 기준 대학 목록 조회 완료"), FIELD_MAJOR_LIST_BY_FIELD_SUCCESS(HttpStatus.OK, "FIELD_MAJOR_2007", "계열 기준 전공 목록 조회 완료"), + MAJOR_UNIV_BOOKMARK_LIST_SUCCESS(HttpStatus.OK, "UNIV_MAJOR_2008", "전공+대학 북마크 목록 조회 완료"), // 내가 추가한 코드 TOKEN_PROCESS_SUCCESS(HttpStatus.OK, "TOKEN_2001", "코인(토큰) 사용이 완료되었습니다."); diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/dto/UnivMajorTupleDto.java b/src/main/java/hackerthon/likelion13th/canfly/search/dto/UnivMajorTupleDto.java new file mode 100644 index 0000000..c12af35 --- /dev/null +++ b/src/main/java/hackerthon/likelion13th/canfly/search/dto/UnivMajorTupleDto.java @@ -0,0 +1,17 @@ +package hackerthon.likelion13th.canfly.search.dto; + + +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class UnivMajorTupleDto { + private Long majorId; + private Long univId; + private List name; +} \ No newline at end of file diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/repository/FieldBookmarkRepository.java b/src/main/java/hackerthon/likelion13th/canfly/search/repository/FieldBookmarkRepository.java index 52c33c1..3a8d9de 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/search/repository/FieldBookmarkRepository.java +++ b/src/main/java/hackerthon/likelion13th/canfly/search/repository/FieldBookmarkRepository.java @@ -3,7 +3,7 @@ import hackerthon.likelion13th.canfly.domain.field.Field; import hackerthon.likelion13th.canfly.domain.field.FieldBookmark; import hackerthon.likelion13th.canfly.domain.user.User; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorBookmarkRepository.java b/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorBookmarkRepository.java index 0ffe614..1c2dea7 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorBookmarkRepository.java +++ b/src/main/java/hackerthon/likelion13th/canfly/search/repository/MajorBookmarkRepository.java @@ -4,7 +4,7 @@ import hackerthon.likelion13th.canfly.domain.major.MajorBookmark; import hackerthon.likelion13th.canfly.domain.university.University; import hackerthon.likelion13th.canfly.domain.user.User; -import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.repository.query.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -50,4 +50,22 @@ ORDER BY COALESCE(MAX(mb.created_at), '1000-01-01 00:00:00') DESC, MAX(mb.mb_id) DESC """, nativeQuery = true) List findAllLikedMajors(@Param("userId") String userId); + + @Query(value = """ + SELECT mb.m_id AS majorId, + mb.univ_id AS univId, + u.univ_name AS universityName, + m.m_name AS majorName, + MAX(mb.created_at) AS lastCreatedAt, -- 정렬용 + MAX(mb.mb_id) AS lastMbId -- 보조 정렬 + FROM major_bookmark mb + JOIN university u ON u.univ_id = mb.univ_id + JOIN major m ON m.m_id = mb.m_id + WHERE mb.uid = :userId + AND mb.univ_id IS NOT NULL + GROUP BY mb.m_id, mb.univ_id, u.univ_name, m.m_name + ORDER BY COALESCE(MAX(mb.created_at), '1000-01-01 00:00:00') DESC, + MAX(mb.mb_id) DESC + """, nativeQuery = true) + List findAllBookmarkedMajorUnivPairs(@Param("userId") String userId); } diff --git a/src/main/java/hackerthon/likelion13th/canfly/search/service/MajorService.java b/src/main/java/hackerthon/likelion13th/canfly/search/service/MajorService.java index 6f2f65b..c725403 100644 --- a/src/main/java/hackerthon/likelion13th/canfly/search/service/MajorService.java +++ b/src/main/java/hackerthon/likelion13th/canfly/search/service/MajorService.java @@ -7,6 +7,7 @@ import hackerthon.likelion13th.canfly.global.exception.GeneralException; import hackerthon.likelion13th.canfly.search.dto.MajorDto; import hackerthon.likelion13th.canfly.search.dto.UnivDto; +import hackerthon.likelion13th.canfly.search.dto.UnivMajorTupleDto; import hackerthon.likelion13th.canfly.search.repository.MajorBookmarkRepository; import hackerthon.likelion13th.canfly.search.repository.MajorRepository; import hackerthon.likelion13th.canfly.university.repository.UniversityRepository; @@ -80,4 +81,21 @@ public List getUniversitiesByMajor(Long majorId) { )) .toList(); } + + public List getAllBookmarkedMajorUnivPairs(User user) { + List rows = majorBookmarkRepository.findAllBookmarkedMajorUnivPairs(user.getUid()); + return rows.stream() + .map(r -> { + Long mId = ((Number) r[0]).longValue(); + Long univId = ((Number) r[1]).longValue(); + String univName = (String) r[2]; + String majorName= (String) r[3]; + return UnivMajorTupleDto.builder() + .majorId(mId) + .univId(univId) + .name(List.of(univName, majorName)) + .build(); + }) + .toList(); + } }