Skip to content
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

Feat: 공지, 새소식, 세미나 정렬 옵션 추가 #247

Merged
merged 8 commits into from
Apr 6, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.wafflestudio.csereal.common.enums

import com.wafflestudio.csereal.common.CserealException

enum class ContentSearchSortType {
DATE,
RELEVANCE;

companion object {
fun fromJsonValue(field: String) =
try {
field.replace('-', '_')
.uppercase()
.let { ContentSearchSortType.valueOf(it) }
} catch (e: IllegalArgumentException) {
throw CserealException.Csereal400("잘못된 Sort Type이 주어졌습니다.")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wafflestudio.csereal.core.news.api

import com.wafflestudio.csereal.common.aop.AuthenticatedStaff
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.common.utils.getUsername
import com.wafflestudio.csereal.core.news.dto.NewsDto
import com.wafflestudio.csereal.core.news.dto.NewsSearchResponse
Expand Down Expand Up @@ -31,6 +32,7 @@ class NewsController(
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) pageNum: Int?,
@RequestParam(required = false, defaultValue = "10") pageSize: Int,
@RequestParam(required = false, defaultValue = "DATE") sortBy: String,
authentication: Authentication?
): ResponseEntity<NewsSearchResponse> {
val username = getUsername(authentication)
Expand All @@ -42,7 +44,10 @@ class NewsController(
val usePageBtn = pageNum != null
val page = pageNum ?: 1
val pageRequest = PageRequest.of(page - 1, pageSize)
return ResponseEntity.ok(newsService.searchNews(tag, keyword, pageRequest, usePageBtn, isStaff))

val sortType = ContentSearchSortType.fromJsonValue(sortBy)

return ResponseEntity.ok(newsService.searchNews(tag, keyword, pageRequest, usePageBtn, sortType, isStaff))
}

@GetMapping("/totalSearch")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.wafflestudio.csereal.core.news.database

import com.querydsl.core.BooleanBuilder
import com.querydsl.jpa.impl.JPAQueryFactory
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.common.repository.CommonRepository
import com.wafflestudio.csereal.common.utils.FixedPageRequest
import com.wafflestudio.csereal.core.admin.dto.AdminSlideElement
Expand Down Expand Up @@ -39,6 +40,7 @@ interface CustomNewsRepository {
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NewsSearchResponse

Expand All @@ -64,6 +66,7 @@ class NewsRepositoryImpl(
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NewsSearchResponse {
val keywordBooleanBuilder = BooleanBuilder()
Expand Down Expand Up @@ -109,12 +112,17 @@ class NewsRepositoryImpl(
total = (10 * pageable.pageSize).toLong() + 1 // 10개 페이지 고정
}

val newsEntityList = jpaQuery
.orderBy(newsEntity.createdAt.desc())
val newsEntityQuery = jpaQuery
.offset(pageRequest.offset)
.limit(pageRequest.pageSize.toLong())
.distinct()
.fetch()

val newsEntityList = when {
sortBy == ContentSearchSortType.DATE || keyword.isNullOrEmpty() -> newsEntityQuery.orderBy(
newsEntity.createdAt.desc()
)
else /* sortBy == RELEVANCE */ -> newsEntityQuery
}.fetch()

val newsSearchDtoList: List<NewsSearchDto> = newsEntityList.map {
val imageURL = mainImageService.createImageURL(it.mainImage)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wafflestudio.csereal.core.news.service

import com.wafflestudio.csereal.common.CserealException
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.core.admin.dto.AdminSlidesResponse
import com.wafflestudio.csereal.core.news.database.*
import com.wafflestudio.csereal.core.news.dto.NewsDto
Expand All @@ -20,6 +21,7 @@ interface NewsService {
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NewsSearchResponse

Expand Down Expand Up @@ -53,9 +55,10 @@ class NewsServiceImpl(
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NewsSearchResponse {
return newsRepository.searchNews(tag, keyword, pageable, usePageBtn, isStaff)
return newsRepository.searchNews(tag, keyword, pageable, usePageBtn, sortBy, isStaff)
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wafflestudio.csereal.core.notice.api

import com.wafflestudio.csereal.common.aop.AuthenticatedStaff
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.common.utils.getUsername
import com.wafflestudio.csereal.core.notice.dto.*
import com.wafflestudio.csereal.core.notice.service.NoticeService
Expand Down Expand Up @@ -29,6 +30,7 @@ class NoticeController(
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) pageNum: Int?,
@RequestParam(required = false, defaultValue = "20") pageSize: Int,
@RequestParam(required = false, defaultValue = "DATE") sortBy: String,
authentication: Authentication?
): ResponseEntity<NoticeSearchResponse> {
val username = getUsername(authentication)
Expand All @@ -40,7 +42,10 @@ class NoticeController(
val usePageBtn = pageNum != null
val page = pageNum ?: 1
val pageRequest = PageRequest.of(page - 1, pageSize)
return ResponseEntity.ok(noticeService.searchNotice(tag, keyword, pageRequest, usePageBtn, isStaff))

val sortType = ContentSearchSortType.fromJsonValue(sortBy)

return ResponseEntity.ok(noticeService.searchNotice(tag, keyword, pageRequest, usePageBtn, sortType, isStaff))
}

@GetMapping("/totalSearch")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.wafflestudio.csereal.core.notice.database
import com.querydsl.core.BooleanBuilder
import com.querydsl.core.types.Projections
import com.querydsl.jpa.impl.JPAQueryFactory
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.common.repository.CommonRepository
import com.wafflestudio.csereal.common.utils.FixedPageRequest
import com.wafflestudio.csereal.core.notice.database.QNoticeEntity.noticeEntity
Expand Down Expand Up @@ -35,6 +36,7 @@ interface CustomNoticeRepository {
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NoticeSearchResponse

Expand Down Expand Up @@ -95,21 +97,13 @@ class NoticeRepositoryImpl(
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NoticeSearchResponse {
val keywordBooleanBuilder = BooleanBuilder()
val tagsBooleanBuilder = BooleanBuilder()
val isPrivateBooleanBuilder = BooleanBuilder()

if (!keyword.isNullOrEmpty()) {
val booleanTemplate = commonRepository.searchFullDoubleTextTemplate(
keyword,
noticeEntity.title,
noticeEntity.plainTextDescription
)
keywordBooleanBuilder.and(booleanTemplate.gt(0.0))
}

if (!tag.isNullOrEmpty()) {
tag.forEach {
val tagEnum = TagInNoticeEnum.getTagEnum(it)
Expand All @@ -125,6 +119,20 @@ class NoticeRepositoryImpl(
)
}

val scoreOrNull = if (!keyword.isNullOrEmpty()) {
commonRepository.searchFullDoubleTextTemplate(
keyword,
noticeEntity.title,
noticeEntity.plainTextDescription
)
} else {
null
}

if (scoreOrNull != null) {
keywordBooleanBuilder.and(scoreOrNull.gt(0.0))
}

val jpaQuery = queryFactory.select(
Projections.constructor(
NoticeSearchDto::class.java,
Expand All @@ -133,7 +141,8 @@ class NoticeRepositoryImpl(
noticeEntity.createdAt,
noticeEntity.isPinned,
noticeEntity.attachments.isNotEmpty,
noticeEntity.isPrivate
noticeEntity.isPrivate,
scoreOrNull
)
).from(noticeEntity)
.leftJoin(noticeTagEntity).on(noticeTagEntity.notice.eq(noticeEntity))
Expand All @@ -151,13 +160,24 @@ class NoticeRepositoryImpl(
total = (10 * pageable.pageSize).toLong() + 1 // 10개 페이지 고정
}

val noticeSearchDtoList = jpaQuery
.orderBy(noticeEntity.isPinned.desc())
.orderBy(noticeEntity.createdAt.desc())
val noticeSearchQuery = jpaQuery
.offset(pageRequest.offset)
.limit(pageRequest.pageSize.toLong())
.distinct()
.fetch()

val noticeSearchDtoList = noticeSearchQuery
.orderBy(noticeEntity.isPinned.desc())
.let {
when {
sortBy == ContentSearchSortType.DATE || scoreOrNull == null -> {
it.orderBy(noticeEntity.createdAt.desc())
}

else -> {
it.orderBy(scoreOrNull.desc())
}
}
}.fetch()

return NoticeSearchResponse(total, noticeSearchDtoList)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ import com.querydsl.core.annotations.QueryProjection
import com.wafflestudio.csereal.core.notice.database.NoticeEntity
import java.time.LocalDateTime

data class NoticeSearchDto @QueryProjection constructor(
data class NoticeSearchDto(
val id: Long,
val title: String,
val createdAt: LocalDateTime?,
val isPinned: Boolean,
val hasAttachment: Boolean,
val isPrivate: Boolean
) {
@QueryProjection constructor(
id: Long,
title: String,
createdAt: LocalDateTime?,
isPinned: Boolean,
hasAttachment: Boolean,
isPrivate: Boolean,
score: Double?
) : this(id, title, createdAt, isPinned, hasAttachment, isPrivate)

constructor(entity: NoticeEntity, hasAttachment: Boolean) : this(
entity.id,
entity.title,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wafflestudio.csereal.core.notice.service

import com.wafflestudio.csereal.common.CserealException
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.common.utils.cleanTextFromHtml
import com.wafflestudio.csereal.core.notice.database.*
import com.wafflestudio.csereal.core.notice.dto.*
Expand All @@ -21,6 +22,7 @@ interface NoticeService {
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NoticeSearchResponse

Expand Down Expand Up @@ -55,9 +57,10 @@ class NoticeServiceImpl(
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): NoticeSearchResponse {
return noticeRepository.searchNotice(tag, keyword, pageable, usePageBtn, isStaff)
return noticeRepository.searchNotice(tag, keyword, pageable, usePageBtn, sortBy, isStaff)
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.wafflestudio.csereal.core.seminar.api

import com.wafflestudio.csereal.common.aop.AuthenticatedStaff
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.common.utils.getUsername
import com.wafflestudio.csereal.core.seminar.dto.SeminarDto
import com.wafflestudio.csereal.core.seminar.dto.SeminarSearchResponse
Expand All @@ -25,6 +26,7 @@ class SeminarController(
@RequestParam(required = false) keyword: String?,
@RequestParam(required = false) pageNum: Int?,
@RequestParam(required = false, defaultValue = "10") pageSize: Int,
@RequestParam(required = false, defaultValue = "DATE") sortBy: String,
authentication: Authentication?
): ResponseEntity<SeminarSearchResponse> {
val username = getUsername(authentication)
Expand All @@ -36,7 +38,10 @@ class SeminarController(
val usePageBtn = pageNum != null
val page = pageNum ?: 1
val pageRequest = PageRequest.of(page - 1, pageSize)
return ResponseEntity.ok(seminarService.searchSeminar(keyword, pageRequest, usePageBtn, isStaff))

val sortType = ContentSearchSortType.fromJsonValue(sortBy)

return ResponseEntity.ok(seminarService.searchSeminar(keyword, pageRequest, usePageBtn, sortType, isStaff))
}

@AuthenticatedStaff
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.wafflestudio.csereal.core.seminar.database

import com.querydsl.core.BooleanBuilder
import com.querydsl.jpa.impl.JPAQueryFactory
import com.wafflestudio.csereal.common.enums.ContentSearchSortType
import com.wafflestudio.csereal.common.repository.CommonRepository
import com.wafflestudio.csereal.common.utils.FixedPageRequest
import com.wafflestudio.csereal.core.resource.mainImage.service.MainImageService
Expand Down Expand Up @@ -29,6 +30,7 @@ interface CustomSeminarRepository {
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): SeminarSearchResponse
}
Expand All @@ -43,6 +45,7 @@ class SeminarRepositoryImpl(
keyword: String?,
pageable: Pageable,
usePageBtn: Boolean,
sortBy: ContentSearchSortType,
isStaff: Boolean
): SeminarSearchResponse {
val keywordBooleanBuilder = BooleanBuilder()
Expand Down Expand Up @@ -83,11 +86,16 @@ class SeminarRepositoryImpl(
total = (10 * pageable.pageSize).toLong() + 1 // 10개 페이지 고정
}

val seminarEntityList = jpaQuery
.orderBy(seminarEntity.createdAt.desc())
val seminarEntityQuery = jpaQuery
.offset(pageRequest.offset)
.limit(pageRequest.pageSize.toLong())
.fetch()

val seminarEntityList = when {
sortBy == ContentSearchSortType.DATE || keyword.isNullOrEmpty() -> seminarEntityQuery.orderBy(
seminarEntity.createdAt.desc()
)
else /* sortBy == RELEVANCE */ -> seminarEntityQuery
}.fetch()

val seminarSearchDtoList: MutableList<SeminarSearchDto> = mutableListOf()

Expand Down
Loading
Loading