Skip to content

태그 기반 고등학생 질문글 추천 API 구현 #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 3, 2024
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
2 changes: 1 addition & 1 deletion GD-YD-BE-Config
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@
import com.gdyd.gdydapi.request.mentoring.CreateHighSchoolStudentQuestionRequest;
import com.gdyd.gdydapi.request.mentoring.CreateUniversityStudentAnswerRequest;
import com.gdyd.gdydapi.request.report.ReportRequest;
import com.gdyd.gdydapi.response.board.GetPostSummaryResponse;
import com.gdyd.gdydapi.response.common.LikeListResponse;
import com.gdyd.gdydapi.response.common.PageResponse;
import com.gdyd.gdydapi.response.common.ScrapListResponse;
import com.gdyd.gdydapi.response.common.ReportResponse;
import com.gdyd.gdydapi.response.mentoring.CreateHighSchoolStudentQuestionResponse;
import com.gdyd.gdydapi.response.mentoring.CreateUniversityStudentAnswerResponse;
import com.gdyd.gdydapi.response.mentoring.DetailHighSchoolStudentQuestionResponse;
import com.gdyd.gdydapi.response.mentoring.HighSchoolStudentQuestionResponse;
import com.gdyd.gdydapi.response.common.ScrapListResponse;
import com.gdyd.gdydapi.response.mentoring.*;
import com.gdyd.gdydapi.service.mentoring.MentoringCommandService;
import com.gdyd.gdydapi.service.mentoring.MentoringQueryService;
import io.swagger.v3.oas.annotations.Operation;
Expand Down Expand Up @@ -170,4 +166,11 @@ public ResponseEntity<ReportResponse> reportUniversityStudentAnswer(@PathVariabl
ReportResponse response = mentoringCommandService.reportUniversityStudentAnswer(universityStudentAnswerId, request);
return ResponseEntity.ok(response);
}

@Operation(summary = "고등학생 질문 대학생 매칭 API", description = "현재 대학생의 정보를 바탕으로 고등학생 질문글 태그와 매칭되는 질문글을 조회하는 API")
@GetMapping("/high-school-student-question/recommendation/tags")
public ResponseEntity<PageResponse<RecommendationHighSchoolStudentQuestionResponse>> getTopHighSchoolStudentQuestionsByTags() {
PageResponse<RecommendationHighSchoolStudentQuestionResponse> response = mentoringQueryService.getTopQuestionByTagScore();
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.gdyd.gdydapi.response.mentoring;

import com.gdyd.gdydcore.domain.mentoring.HighSchoolStudentQuestion;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Builder
@Schema(description = "고등학생 질문 매칭 추천 응답")
public record RecommendationHighSchoolStudentQuestionResponse(
@Schema(description = "매칭된 고등학생 질문글 ID", example = "1")
Long id,

@Schema(description = "매칭된 고등학생 질문글 제목", example = "제가 이 학교를 갈 수 있을까요?")
String title,

@Schema(description = "매칭된 고등학생 질문글 내용", example = "현재 수능 23225 입니다.")
String question
) {
public static RecommendationHighSchoolStudentQuestionResponse from(HighSchoolStudentQuestion highSchoolStudentQuestion) {
return RecommendationHighSchoolStudentQuestionResponse.builder()
.id(highSchoolStudentQuestion.getId())
.title(highSchoolStudentQuestion.getTitle())
.question(highSchoolStudentQuestion.getQuestion())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
import com.gdyd.gdydapi.response.common.PageResponse;
import com.gdyd.gdydapi.response.mentoring.DetailHighSchoolStudentQuestionResponse;
import com.gdyd.gdydapi.response.mentoring.HighSchoolStudentQuestionResponse;
import com.gdyd.gdydapi.response.mentoring.RecommendationHighSchoolStudentQuestionResponse;
import com.gdyd.gdydauth.utils.PrincipalUtil;
import com.gdyd.gdydcore.domain.member.UniversityStudent;
import com.gdyd.gdydcore.domain.mentoring.HighSchoolStudentQuestion;
import com.gdyd.gdydcore.domain.mentoring.UniversityStudentAnswer;
import com.gdyd.gdydcore.service.member.UniversityStudentService;
import com.gdyd.gdydcore.service.mentoring.HighSchoolStudentQuestionService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -20,6 +25,7 @@
@Transactional(readOnly = true)
public class MentoringQueryService {
private final HighSchoolStudentQuestionService highSchoolStudentQuestionService;
private final UniversityStudentService universityStudentService;

/**
* 고등학생 질문 목록 조회 (페이지네이션)
Expand All @@ -44,4 +50,25 @@ public DetailHighSchoolStudentQuestionResponse getHighSchoolStudentQuestionDetai
List<UniversityStudentAnswer> universityStudentAnswers = highSchoolStudentQuestion.getUniversityStudentAnswers();
return DetailHighSchoolStudentQuestionResponse.of(highSchoolStudentQuestion, universityStudentAnswers);
}

/**
* 대학생 정보와 고등학생 질문글 태그를 바탕으로 대학생에게 추천할 고등학생 질문글 목록 조회
* Page를 사용해서 최근 7일간 작성된 질문글과 대학생 정보를 바탕으로 점수를 계산하여 상위 5개의 질문글을 추천
*/
public PageResponse<RecommendationHighSchoolStudentQuestionResponse> getTopQuestionByTagScore() {
Long memberId = PrincipalUtil.getMemberIdByPrincipal();
UniversityStudent universityStudent = universityStudentService.getUniversityStudentByMemberId(memberId);

LocalDateTime cutoffDate = LocalDateTime.now().minusDays(7);
Pageable pageable = PageRequest.of(0, 5);
List<HighSchoolStudentQuestion> questions = highSchoolStudentQuestionService.findTopQuestionsByScore(
universityStudent.getUniversityName(),
universityStudent.getUniversityMajorCategory(),
universityStudent.getUniversityGrade(),
cutoffDate,
pageable
);

return PageResponse.of(questions.stream().map(RecommendationHighSchoolStudentQuestionResponse::from).toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ public class HighSchoolStudentQuestion extends BaseTimeEntity {
String universityNameTag;

@Column(nullable = false)
@Enumerated(EnumType.STRING)
UniversityMajorCategory universityMajorTag;

@Column(nullable = false)
@Enumerated(EnumType.STRING)
Grade universityGradeTag;

@ManyToOne(fetch = FetchType.LAZY)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
package com.gdyd.gdydcore.repository.mentoring;

import com.gdyd.gdydcore.domain.member.Grade;
import com.gdyd.gdydcore.domain.member.UniversityMajorCategory;
import com.gdyd.gdydcore.domain.mentoring.HighSchoolStudentQuestion;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDateTime;
import java.util.List;

public interface HighSchoolStudentQuestionRepository extends JpaRepository<HighSchoolStudentQuestion, Long> {
List<HighSchoolStudentQuestion> findAllByCreatedAtIsAfter(LocalDateTime weeksAgo, Pageable pageable);

@Query("SELECT q FROM HighSchoolStudentQuestion q " +
"WHERE q.createdAt > :cutoffDate " +
"ORDER BY (" +
"CASE WHEN q.universityNameTag = :universityNameTag THEN 0.3 ELSE 0 END + " +
"CASE WHEN q.universityMajorTag = :universityMajorTag THEN 0.6 ELSE 0 END + " +
"CASE WHEN q.universityGradeTag = :universityGradeTag THEN 0.1 ELSE 0 END" +
") DESC")
List<HighSchoolStudentQuestion> findTopQuestionsByTag(
@Param("universityNameTag") String universityNameTag,
@Param("universityMajorTag") UniversityMajorCategory universityMajorTag,
@Param("universityGradeTag") Grade universityGradeTag,
@Param("cutoffDate") LocalDateTime cutoffDate,
Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.gdyd.gdydcore.service.mentoring;

import com.gdyd.gdydcore.domain.member.Grade;
import com.gdyd.gdydcore.domain.member.UniversityMajorCategory;
import com.gdyd.gdydcore.domain.mentoring.HighSchoolStudentQuestion;
import com.gdyd.gdydcore.repository.mentoring.HighSchoolStudentQuestionRepository;
import com.gdyd.gdydsupport.exception.BusinessException;
Expand Down Expand Up @@ -32,6 +34,10 @@ public List<HighSchoolStudentQuestion> findAllByCreatedAtIsAfter(LocalDateTime w
return highSchoolStudentQuestionRepository.findAllByCreatedAtIsAfter(weeksAgo, pageable);
}

public List<HighSchoolStudentQuestion> findTopQuestionsByScore(String universityNameTag, UniversityMajorCategory universityMajorTag, Grade universityGradeTag, LocalDateTime cutoffDate, Pageable pageable) {
return highSchoolStudentQuestionRepository.findTopQuestionsByTag(universityNameTag, universityMajorTag, universityGradeTag, cutoffDate, pageable);
}

@Transactional
public void save(HighSchoolStudentQuestion highSchoolStudentQuestion) {
highSchoolStudentQuestionRepository.save(highSchoolStudentQuestion);
Expand Down
Loading