Skip to content

Commit

Permalink
[BE] feat: 관리자 축제 조회 기능 추가 (#745) (#749)
Browse files Browse the repository at this point in the history
* feat: 관리자 축제 조회 기능 추가

* feat: 관리자 축제 조회 API 추가
  • Loading branch information
seokjin8678 authored Mar 5, 2024
1 parent c41210d commit 19faf78
Show file tree
Hide file tree
Showing 8 changed files with 540 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.festago.admin.application;

import com.festago.admin.dto.AdminFestivalV1Response;
import com.festago.admin.repository.AdminFestivalV1QueryDslRepository;
import com.festago.common.querydsl.SearchCondition;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AdminFestivalV1QueryService {

private final AdminFestivalV1QueryDslRepository adminFestivalV1QueryDslRepository;

public Page<AdminFestivalV1Response> findAll(SearchCondition searchCondition) {
return adminFestivalV1QueryDslRepository.findAll(searchCondition);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.festago.admin.dto;

import com.querydsl.core.annotations.QueryProjection;
import java.time.LocalDate;

public record AdminFestivalV1Response(
Long id,
String name,
String schoolName,
LocalDate startDate,
LocalDate endDate,
long stageCount
) {

@QueryProjection
public AdminFestivalV1Response {
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package com.festago.admin.presentation.v1;

import com.festago.admin.application.AdminFestivalV1QueryService;
import com.festago.admin.dto.AdminFestivalV1Response;
import com.festago.admin.dto.FestivalV1CreateRequest;
import com.festago.admin.dto.FestivalV1UpdateRequest;
import com.festago.common.aop.ValidPageable;
import com.festago.common.querydsl.SearchCondition;
import com.festago.festival.application.command.FestivalCommandFacadeService;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.validation.Valid;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand All @@ -22,8 +31,20 @@
@Hidden
public class AdminFestivalV1Controller {

private final AdminFestivalV1QueryService adminFestivalV1QueryService;
private final FestivalCommandFacadeService festivalCommandFacadeService;

@ValidPageable(maxSize = 50)
@GetMapping
public ResponseEntity<Page<AdminFestivalV1Response>> findAll(
@RequestParam(defaultValue = "") String searchFilter,
@RequestParam(defaultValue = "") String searchKeyword,
@PageableDefault(size = 10) Pageable pageable
) {
return ResponseEntity.ok()
.body(adminFestivalV1QueryService.findAll(new SearchCondition(searchFilter, searchKeyword, pageable)));
}

@PostMapping
public ResponseEntity<Void> createFestival(
@RequestBody @Valid FestivalV1CreateRequest request
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.festago.admin.repository;

import static com.festago.festival.domain.QFestival.festival;
import static com.festago.school.domain.QSchool.school;
import static com.festago.stage.domain.QStage.stage;

import com.festago.admin.dto.AdminFestivalV1Response;
import com.festago.admin.dto.QAdminFestivalV1Response;
import com.festago.common.querydsl.OrderSpecifierUtils;
import com.festago.common.querydsl.QueryDslHelper;
import com.festago.common.querydsl.SearchCondition;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

@Repository
@RequiredArgsConstructor
public class AdminFestivalV1QueryDslRepository {

private final QueryDslHelper queryDslHelper;

public Page<AdminFestivalV1Response> findAll(SearchCondition searchCondition) {
Pageable pageable = searchCondition.pageable();
String searchFilter = searchCondition.searchFilter();
String searchKeyword = searchCondition.searchKeyword();
return queryDslHelper.applyPagination(pageable,
queryFactory -> queryFactory.select(
new QAdminFestivalV1Response(
festival.id,
festival.name,
school.name,
festival.startDate,
festival.endDate,
stage.count()
))
.from(festival)
.innerJoin(school).on(school.id.eq(festival.school.id))
.leftJoin(stage).on(stage.festival.id.eq(festival.id))
.where(applySearchFilter(searchFilter, searchKeyword))
.groupBy(festival.id)
.orderBy(getOrderSpecifier(pageable.getSort()))
.offset(pageable.getOffset())
.limit(pageable.getPageSize()),
queryFactory -> queryFactory.select(festival.count())
.from(festival)
.where(applySearchFilter(searchFilter, searchKeyword)));
}

private BooleanExpression applySearchFilter(String searchFilter, String searchKeyword) {
return switch (searchFilter) {
case "id" -> eqId(searchKeyword);
case "name" -> containsName(searchKeyword);
case "schoolName" -> containsSchoolName(searchKeyword);
default -> null;
};
}

private BooleanExpression eqId(String searchKeyword) {
if (StringUtils.hasText(searchKeyword)) {
return festival.id.stringValue().eq(searchKeyword);
}
return null;
}

private BooleanExpression containsName(String searchKeyword) {
if (StringUtils.hasText(searchKeyword)) {
return festival.name.contains(searchKeyword);
}
return null;
}

private BooleanExpression containsSchoolName(String searchKeyword) {
if (StringUtils.hasText(searchKeyword)) {
return school.name.contains(searchKeyword);
}
return null;
}

private OrderSpecifier<?> getOrderSpecifier(Sort sort) {
return sort.stream()
.findFirst()
.map(it -> switch (it.getProperty()) {
case "id" -> OrderSpecifierUtils.of(it.getDirection(), festival.id);
case "name" -> OrderSpecifierUtils.of(it.getDirection(), festival.name);
case "schoolName" -> OrderSpecifierUtils.of(it.getDirection(), school.name);
case "startDate" -> OrderSpecifierUtils.of(it.getDirection(), festival.startDate);
case "endDate" -> OrderSpecifierUtils.of(it.getDirection(), festival.endDate);
default -> OrderSpecifierUtils.NULL;
})
.orElse(OrderSpecifierUtils.NULL);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.festago.common.querydsl;

import com.querydsl.core.types.Expression;
import com.querydsl.core.types.NullExpression;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import org.springframework.data.domain.Sort;

public class OrderSpecifierUtils {

public static final OrderSpecifier<?> NULL = new OrderSpecifier(Order.ASC, NullExpression.DEFAULT,
OrderSpecifier.NullHandling.Default);

private OrderSpecifierUtils() {
}

public static OrderSpecifier of(Sort.Direction direction, Expression<?> target) {
return new OrderSpecifier(direction.isAscending() ? Order.ASC : Order.DESC, target);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.festago.common.querydsl;

import com.querydsl.core.types.Expression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class QueryDslHelper {

private final JPAQueryFactory queryFactory;

public <T> JPAQuery<T> select(Expression<T> expr) {
return queryFactory.select(expr);
}

public <T> Optional<T> fetchOne(Function<JPAQueryFactory, JPAQuery<T>> queryFunction) {
JPAQuery<T> query = queryFunction.apply(queryFactory);
return Optional.ofNullable(query.fetchOne());
}

public <T> Page<T> applyPagination(
Pageable pageable,
Function<JPAQueryFactory, JPAQuery<T>> contentQueryFunction,
Function<JPAQueryFactory, JPAQuery<Long>> countQueryFunction
) {
List<T> content = contentQueryFunction.apply(queryFactory).fetch();
JPAQuery<Long> countQuery = countQueryFunction.apply(queryFactory);
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
}
}
Loading

0 comments on commit 19faf78

Please sign in to comment.