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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.backend.elearning.domain.course;

import com.backend.elearning.domain.category.Category;
import com.backend.elearning.domain.common.PageableData;
import com.backend.elearning.domain.review.Review;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

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

@Repository
public class CourseRepositoryCustomImpl {


@PersistenceContext
private EntityManager entityManager;

public Page<Course> findByMultiFilter(
String title,
Float ratingStar,
String[] level,
Boolean[] free,
String categoryName,
Integer topicId,
Pageable pageable) {

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

// ----------- MAIN QUERY -------------
CriteriaQuery<Course> cq = cb.createQuery(Course.class);
Root<Course> root = cq.from(Course.class);
root.fetch("category", JoinType.INNER).fetch("parent", JoinType.LEFT);
root.fetch("topic", JoinType.INNER);
root.fetch("user", JoinType.INNER);
cq.distinct(true);

List<Predicate> predicates = new ArrayList<>();

// level IN (...)
if (level != null && level.length > 0) {
CriteriaBuilder.In<String> levelIn = cb.in(root.get("level"));
for (String l : level) {
levelIn.value(l);
}
predicates.add(levelIn);
}

// free IN (...)
if (free != null && free.length > 0) {
CriteriaBuilder.In<Boolean> freeIn = cb.in(root.get("free"));
for (Boolean f : free) {
freeIn.value(f);
}
predicates.add(freeIn);
}

if (title != null && !title.isBlank()) {
predicates.add(cb.like(cb.lower(root.get("title")), "%" + title.toLowerCase() + "%"));
}

// categoryName = cat.name or parent.name
if (categoryName != null) {
Join<Course, Category> categoryJoin = root.join("category");
Join<Category, Category> parentJoin = categoryJoin.join("parent", JoinType.LEFT);
Predicate matchCat = cb.equal(categoryJoin.get("name"), categoryName);
Predicate matchParent = cb.equal(parentJoin.get("name"), categoryName);
predicates.add(cb.or(matchCat, matchParent));
}

// topicId
if (topicId != null) {
predicates.add(cb.equal(root.get("topic").get("id"), topicId));
}

// status = 'PUBLISHED'
predicates.add(cb.equal(root.get("status"), "PUBLISHED"));

// Subquery for avg rating
if (ratingStar != null) {
Subquery<Double> subquery = cq.subquery(Double.class);
Root<Review> reviewRoot = subquery.from(Review.class);
subquery.select(cb.avg(reviewRoot.get("ratingStar")));
subquery.where(cb.equal(reviewRoot.get("course").get("id"), root.get("id")));
predicates.add(cb.greaterThanOrEqualTo(cb.coalesce(subquery, 0d), ratingStar.doubleValue()));
}

cq.where(cb.and(predicates.toArray(new Predicate[0])));

// ----------- EXECUTE MAIN QUERY -------------
TypedQuery<Course> query = entityManager.createQuery(cq);
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
List<Course> courses = query.getResultList();

// ----------- COUNT QUERY -------------
CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
Root<Course> countRoot = countQuery.from(Course.class);
countQuery.select(cb.countDistinct(countRoot));

List<Predicate> countPredicates = new ArrayList<>();

// Copy same predicates (repeat manually to avoid fetch joins)
if (level != null && level.length > 0) {
CriteriaBuilder.In<String> levelIn = cb.in(countRoot.get("level"));
for (String l : level) {
levelIn.value(l);
}
countPredicates.add(levelIn);
}

if (title != null && !title.isBlank()) {
countPredicates.add(cb.like(cb.lower(countRoot.get("title")), "%" + title.toLowerCase() + "%"));
}

if (free != null && free.length > 0) {
CriteriaBuilder.In<Boolean> freeIn = cb.in(countRoot.get("free"));
for (Boolean f : free) {
freeIn.value(f);
}
countPredicates.add(freeIn);
}

if (categoryName != null) {
Join<Course, Category> categoryJoin = countRoot.join("category");
Join<Category, Category> parentJoin = categoryJoin.join("parent", JoinType.LEFT);
Predicate matchCat = cb.equal(categoryJoin.get("name"), categoryName);
Predicate matchParent = cb.equal(parentJoin.get("name"), categoryName);
countPredicates.add(cb.or(matchCat, matchParent));
}

if (topicId != null) {
countPredicates.add(cb.equal(countRoot.get("topic").get("id"), topicId));
}

countPredicates.add(cb.equal(countRoot.get("status"), "PUBLISHED"));

if (ratingStar != null) {
Subquery<Double> subquery = countQuery.subquery(Double.class);
Root<Review> reviewRoot = subquery.from(Review.class);
subquery.select(cb.avg(reviewRoot.get("ratingStar")));
subquery.where(cb.equal(reviewRoot.get("course").get("id"), countRoot.get("id")));
countPredicates.add(cb.greaterThanOrEqualTo(cb.coalesce(subquery, 0d), ratingStar.doubleValue()));
}

countQuery.where(cb.and(countPredicates.toArray(new Predicate[0])));
Long total = entityManager.createQuery(countQuery).getSingleResult();

return new PageImpl<>(courses, pageable, total);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import org.springframework.data.domain.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
Expand All @@ -57,13 +56,16 @@ public class CourseServiceImpl implements CourseService{
private final LearningQuizRepository learningQuizRepository;
private final LearningCourseRepository learningCourseRepository;
private final OrderDetailRepository orderDetailRepository;

private final CourseRepositoryCustomImpl courseRepositoryCustom;
private final CartRepository cartRepository;
private final UserService userService;
private final UserRepository userRepository;
private static final String LECTURE_TYPE = "lecture";
private static final String QUIZ_TYPE = "quiz";
private static final String sortBy = "updatedAt";
public CourseServiceImpl(CourseRepository courseRepository, CategoryRepository categoryRepository, TopicRepository topicRepository, SectionService sectionService, QuizRepository quizRepository, LectureRepository lectureRepository, ReviewService reviewService, LearningLectureRepository learningLectureRepository, LearningQuizRepository learningQuizRepository, LearningCourseRepository learningCourseRepository, OrderDetailRepository orderDetailRepository, CartRepository cartRepository, UserService userService, UserRepository userRepository) {

public CourseServiceImpl(CourseRepository courseRepository, CategoryRepository categoryRepository, TopicRepository topicRepository, SectionService sectionService, QuizRepository quizRepository, LectureRepository lectureRepository, ReviewService reviewService, LearningLectureRepository learningLectureRepository, LearningQuizRepository learningQuizRepository, LearningCourseRepository learningCourseRepository, OrderDetailRepository orderDetailRepository, CourseRepositoryCustomImpl courseRepositoryCustom, CartRepository cartRepository, UserService userService, UserRepository userRepository) {
this.courseRepository = courseRepository;
this.categoryRepository = categoryRepository;
this.topicRepository = topicRepository;
Expand All @@ -75,6 +77,7 @@ public CourseServiceImpl(CourseRepository courseRepository, CategoryRepository c
this.learningQuizRepository = learningQuizRepository;
this.learningCourseRepository = learningCourseRepository;
this.orderDetailRepository = orderDetailRepository;
this.courseRepositoryCustom = courseRepositoryCustom;
this.cartRepository = cartRepository;
this.userService = userService;
this.userRepository = userRepository;
Expand Down Expand Up @@ -354,11 +357,8 @@ public PageableData<CourseListGetVM> getCoursesByMultiQuery(int pageNum,
log.info("received pageNum: {}, pageSize: {}, title: {}, rating: {}, level: {}, free: {}, categoryName: {}, " +
"topicId: {}", pageNum, pageSize, title, rating, level, free, categoryName, topicId);
Pageable pageable = PageRequest.of(pageNum, pageSize);
Page<Course> coursePage = title != null ? courseRepository.findByMultiQueryWithKeyword(pageable, title, rating, level, free, categoryName, topicId) :
courseRepository.findByMultiQuery(pageable, rating, level, free, categoryName, topicId);
Page<Course> coursePage = courseRepositoryCustom.findByMultiFilter(title, rating, level, free, categoryName, topicId, pageable);
List<Course> courses = coursePage.getContent();
// List<Course> courses = title != null ? courseRepository.findByMultiQueryWithKeyword(title, rating, level, free, categoryName, topicId) :
// courseRepository.findByMultiQuery(rating, level, free, categoryName, topicId);
List<CourseListGetVM> courseListGetVMS = courses.stream().map(course -> {
course = courseRepository.findByIdWithPromotions(course).orElseThrow(() -> new NotFoundException(Constants.ERROR_CODE.COURSE_NOT_FOUND));
List<Review> reviews = course.getReviews();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public UserVm updateProfileStudent(StudentPutVM studentPutVM) {
if (studentPutVM.photo() != null && !studentPutVM.photo().isEmpty() && !studentPutVM.photo().isBlank()) {
student.setPhoto(studentPutVM.photo());
}
if (studentPutVM.photo() != null && !studentPutVM.password().isEmpty() && !studentPutVM.password().isBlank() ) {
if (studentPutVM.password() != null && !studentPutVM.password().isEmpty() && !studentPutVM.password().isBlank()) {
student.setPassword(passwordEncoder.encode(studentPutVM.password()));
}
// update
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.backend.elearning.service;

import com.backend.elearning.domain.auth.AuthenticationService;
import com.backend.elearning.domain.cart.Cart;
import com.backend.elearning.domain.cart.CartRepository;
import com.backend.elearning.domain.category.Category;
Expand All @@ -22,10 +21,8 @@
import com.backend.elearning.domain.user.User;
import com.backend.elearning.domain.user.UserRepository;
import com.backend.elearning.domain.user.UserService;
import com.backend.elearning.domain.user.UserVm;
import com.backend.elearning.exception.BadRequestException;
import com.backend.elearning.exception.DuplicateException;
import com.backend.elearning.exception.NotFoundException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -83,12 +80,15 @@ public class CourseServiceTest {
@Mock
private SecurityContext securityContext;

@Mock
private CourseRepositoryCustomImpl courseRepositoryCustom;

@Mock
private Authentication authentication;
@BeforeEach
void beforeEach() {
courseService = new CourseServiceImpl(courseRepository, categoryRepository, topicRepository, sectionService,
quizRepository, lectureRepository, reviewService, learningLectureRepository, learningQuizRepository, learningCourseRepository, orderDetailRepository, cartRepository
quizRepository, lectureRepository, reviewService, learningLectureRepository, learningQuizRepository, learningCourseRepository, orderDetailRepository, courseRepositoryCustom, cartRepository
,userService, userRepository);
SecurityContextHolder.setContext(securityContext);

Expand Down
Loading