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
Expand Up @@ -4,6 +4,9 @@ import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import picklab.backend.activity.application.model.ActivitySearchCondition
import picklab.backend.activity.application.model.ActivityView
import picklab.backend.activity.domain.enums.ActivitySortType
import picklab.backend.activity.domain.enums.RecruitmentStatus
import picklab.backend.job.domain.enums.JobGroup

interface ActivityRepositoryCustom {
fun getActivities(
Expand All @@ -21,4 +24,15 @@ interface ActivityRepositoryCustom {
keyword: String,
limit: Int,
): List<String>

fun searchActivitiesByKeyword(
keyword: String,
activityType: String?,
status: RecruitmentStatus?,
jobGroups: List<JobGroup>?,
sort: ActivitySortType,
pageable: PageRequest,
): Page<ActivityView>

fun countActivitiesByKeywordPerType(keyword: String): Map<String, Long>
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import picklab.backend.activity.domain.enums.EducationCostType
import picklab.backend.activity.domain.enums.EducationFormatType
import picklab.backend.activity.domain.enums.LocationType
import picklab.backend.activity.domain.enums.RecruitmentEndType
import picklab.backend.activity.domain.enums.RecruitmentStatus
import picklab.backend.activity.infrastructure.QActivityItem
import picklab.backend.job.domain.entity.QJobCategory
import picklab.backend.job.domain.enums.JobGroup
import java.time.LocalDate

@Repository
Expand Down Expand Up @@ -80,14 +82,18 @@ class ActivityRepositoryImpl(

val orderBy =
when (queryData.sort) {
ActivitySortType.LATEST -> listOf(QActivity.activity.createdAt.desc())
ActivitySortType.DEADLINE_ASC ->
ActivitySortType.LATEST -> {
listOf(QActivity.activity.createdAt.desc())
}

ActivitySortType.DEADLINE_ASC -> {
listOf(
QActivity.activity.recruitmentEndDate.asc(),
QActivity.activity.createdAt.desc(),
)
}

ActivitySortType.DEADLINE_DESC ->
ActivitySortType.DEADLINE_DESC -> {
listOf(
Expressions
.numberTemplate(
Expand All @@ -98,6 +104,7 @@ class ActivityRepositoryImpl(
).desc(),
QActivity.activity.createdAt.desc(),
)
}
}

val items =
Expand Down Expand Up @@ -165,6 +172,150 @@ class ActivityRepositoryImpl(
).orderBy(QActivity.activity.title.asc())
.limit(limit.toLong())
.fetch()

override fun searchActivitiesByKeyword(
keyword: String,
activityType: String?,
status: RecruitmentStatus?,
jobGroups: List<JobGroup>?,
sort: ActivitySortType,
pageable: PageRequest,
): Page<ActivityView> {
val condition =
BooleanBuilder().apply {
and(QActivity.activity.deletedAt.isNull)
and(
QActivity.activity.title
.containsIgnoreCase(keyword)
.or(QActivity.activity.organizer.containsIgnoreCase(keyword)),
)
activityType?.let { and(QActivity.activity.activityType.eq(it)) }
status?.let { and(QActivity.activity.status.eq(it)) }
jobGroups?.let { and(QJobCategory.jobCategory.jobGroup.`in`(it)) }
}

val orderBy =
when (sort) {
ActivitySortType.LATEST -> {
listOf(QActivity.activity.createdAt.desc())
}

ActivitySortType.DEADLINE_ASC -> {
listOf(
QActivity.activity.recruitmentEndDate.asc(),
QActivity.activity.createdAt.desc(),
)
}

ActivitySortType.DEADLINE_DESC -> {
listOf(
Expressions
.numberTemplate(
Long::class.java,
"DATEDIFF({0}, {1})",
QActivity.activity.recruitmentEndDate,
LocalDate.now(),
).desc(),
QActivity.activity.createdAt.desc(),
)
}
}

val activityIds =
jpaQueryFactory
.select(QActivity.activity.id)
.distinct()
.from(QActivity.activity)
.leftJoin(QActivityJobCategory.activityJobCategory)
.on(
QActivityJobCategory.activityJobCategory.activity.id
.eq(QActivity.activity.id),
).leftJoin(QJobCategory.jobCategory)
.on(
QActivityJobCategory.activityJobCategory.jobCategory.id
.eq(QJobCategory.jobCategory.id),
).where(condition)
.orderBy(*orderBy.toTypedArray())
.offset(pageable.offset)
.limit(pageable.pageSize.toLong())
.fetch()

if (activityIds.isEmpty()) {
return PageImpl(emptyList(), pageable, 0)
}

val itemsMap =
jpaQueryFactory
.selectFrom(QActivity.activity)
.leftJoin(QActivityJobCategory.activityJobCategory)
.on(
QActivityJobCategory.activityJobCategory.activity.id
.eq(QActivity.activity.id),
).leftJoin(QJobCategory.jobCategory)
.on(
QActivityJobCategory.activityJobCategory.jobCategory.id
.eq(QJobCategory.jobCategory.id),
).where(QActivity.activity.id.`in`(activityIds))
.transform(
GroupBy.groupBy(QActivity.activity.id).`as`(
QActivityItem(
QActivity.activity.id,
QActivity.activity.title,
QActivity.activity.organizer,
QActivity.activity.organizerType.stringValue(),
QActivity.activity.startDate,
QActivity.activity.activityType,
GroupBy.list(QJobCategory.jobCategory.jobDetail.stringValue()),
QActivity.activity.activityThumbnailUrl,
QActivity.activity.viewCount,
QActivity.activity.recruitmentEndDate,
QActivity.activity.recruitmentEndType,
),
),
)

val items = activityIds.mapNotNull { itemsMap[it] }

val count =
jpaQueryFactory
.select(QActivity.activity.id.countDistinct())
.from(QActivity.activity)
.leftJoin(QActivityJobCategory.activityJobCategory)
.on(
QActivityJobCategory.activityJobCategory.activity.id
.eq(QActivity.activity.id),
).leftJoin(QJobCategory.jobCategory)
.on(
QActivityJobCategory.activityJobCategory.jobCategory.id
.eq(QJobCategory.jobCategory.id),
).where(condition)
.fetchOne() ?: 0L

return PageImpl(items, pageable, count)
}

override fun countActivitiesByKeywordPerType(keyword: String): Map<String, Long> {
val condition =
BooleanBuilder().apply {
and(QActivity.activity.deletedAt.isNull)
and(
QActivity.activity.title
.containsIgnoreCase(keyword)
.or(QActivity.activity.organizer.containsIgnoreCase(keyword)),
)
}

return jpaQueryFactory
.select(QActivity.activity.activityType, QActivity.activity.id.count())
.from(QActivity.activity)
.where(condition)
.groupBy(QActivity.activity.activityType)
.fetch()
.associate { tuple ->
(tuple.get(QActivity.activity.activityType) ?: "") to
(tuple.get(QActivity.activity.id.count()) ?: 0L)
}
}
}

inline fun <T> BooleanBuilder.andIfNotNullOrEmpty(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import picklab.backend.activity.application.ActivityQueryRepository
import picklab.backend.activity.application.model.ActivitySearchCondition
import picklab.backend.activity.application.model.ActivityView
import picklab.backend.activity.domain.entity.Activity
import picklab.backend.activity.domain.enums.ActivitySortType
import picklab.backend.activity.domain.enums.ActivityType
import picklab.backend.activity.domain.enums.EducationFormatType
import picklab.backend.activity.domain.enums.RecruitmentStatus
import picklab.backend.activity.domain.repository.ActivityRepository
import picklab.backend.common.model.BusinessException
import picklab.backend.common.model.ErrorCode
import picklab.backend.job.domain.enums.JobGroup
import java.time.LocalDate

@Service
Expand Down Expand Up @@ -201,4 +203,17 @@ class ActivityService(
* 인기도는 조회수와 북마크 수를 합산하여 계산합니다.
*/
fun getPopularActivities(pageable: PageRequest): Page<ActivityView> = activityQueryRepository.findPopularActivities(pageable)

@Transactional(readOnly = true)
fun searchActivitiesByKeyword(
keyword: String,
activityType: String?,
status: RecruitmentStatus?,
jobGroups: List<JobGroup>?,
sort: ActivitySortType,
pageable: PageRequest,
): Page<ActivityView> = activityRepository.searchActivitiesByKeyword(keyword, activityType, status, jobGroups, sort, pageable)

@Transactional(readOnly = true)
fun countActivitiesByKeywordPerType(keyword: String): Map<String, Long> = activityRepository.countActivitiesByKeywordPerType(keyword)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import picklab.backend.activity.domain.entity.QActivity
import picklab.backend.activity.domain.entity.QActivityBookmark
import picklab.backend.activity.domain.entity.QActivityJobCategory
import picklab.backend.activity.domain.enums.ActivityBookmarkSortType
import picklab.backend.activity.domain.enums.RecruitmentEndType
import picklab.backend.activity.domain.enums.RecruitmentStatus
import picklab.backend.job.domain.entity.QJobCategory
import picklab.backend.member.domain.entity.QMemberActivityViewHistory
import java.time.LocalDate

@Repository
class ActivityQueryRepositoryImpl(
Expand All @@ -42,7 +44,12 @@ class ActivityQueryRepositoryImpl(
.eq(QJobCategory.jobCategory.id),
).where(
QActivity.activity.deletedAt.isNull
.and(QJobCategory.jobCategory.id.`in`(jobIds)),
.and(QJobCategory.jobCategory.id.`in`(jobIds))
.and(
QActivity.activity.recruitmentEndType
.ne(RecruitmentEndType.FIXED)
.or(QActivity.activity.recruitmentEndDate.goe(LocalDate.now())),
),
).groupBy(QActivity.activity.id)
.orderBy(
QActivity.activity.viewCount
Expand Down Expand Up @@ -116,7 +123,12 @@ class ActivityQueryRepositoryImpl(
.eq(QJobCategory.jobCategory.id),
).where(
QActivity.activity.deletedAt.isNull
.and(QJobCategory.jobCategory.id.`in`(jobIds)),
.and(QJobCategory.jobCategory.id.`in`(jobIds))
.and(
QActivity.activity.recruitmentEndType
.ne(RecruitmentEndType.FIXED)
.or(QActivity.activity.recruitmentEndDate.goe(LocalDate.now())),
),
).fetchOne() ?: 0L

return PageImpl(items, pageable, count)
Expand All @@ -127,6 +139,11 @@ class ActivityQueryRepositoryImpl(
BooleanBuilder().apply {
and(QActivity.activity.status.eq(RecruitmentStatus.OPEN))
and(QActivity.activity.deletedAt.isNull)
and(
QActivity.activity.recruitmentEndType
.ne(RecruitmentEndType.FIXED)
.or(QActivity.activity.recruitmentEndDate.goe(LocalDate.now())),
)
}

val orderBy =
Expand Down Expand Up @@ -224,19 +241,27 @@ class ActivityQueryRepositoryImpl(

val orderBy =
when (queryData.sortType) {
ActivityBookmarkSortType.RECENTLY_BOOKMARKED -> listOf(QActivityBookmark.activityBookmark.createdAt.desc())
ActivityBookmarkSortType.LATEST -> listOf(QActivity.activity.createdAt.desc())
ActivityBookmarkSortType.DEADLINE_ASC ->
ActivityBookmarkSortType.RECENTLY_BOOKMARKED -> {
listOf(QActivityBookmark.activityBookmark.createdAt.desc())
}

ActivityBookmarkSortType.LATEST -> {
listOf(QActivity.activity.createdAt.desc())
}

ActivityBookmarkSortType.DEADLINE_ASC -> {
listOf(
QActivity.activity.recruitmentEndDate.asc(),
QActivity.activity.createdAt.desc(),
)
}

ActivityBookmarkSortType.DEADLINE_DESC ->
ActivityBookmarkSortType.DEADLINE_DESC -> {
listOf(
QActivity.activity.recruitmentEndDate.desc(),
QActivity.activity.createdAt.desc(),
)
}
}

val items =
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/picklab/backend/common/model/SuccessCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ enum class SuccessCode(
GET_SATISFACTION_AVG_SCORES_SUCCESS(HttpStatus.OK, "활동별 만족도 통계 조회에 성공했습니다."),

// Search 관련
SEARCH_SUCCESS(HttpStatus.OK, "검색에 성공했습니다."),
SEARCH_ACTIVITIES_SUCCESS(HttpStatus.OK, "검색 결과 조회에 성공했습니다."),
SEARCH_AUTOCOMPLETE_SUCCESS(HttpStatus.OK, "자동완성 검색에 성공했습니다."),
SEARCH_HISTORY_CREATED(HttpStatus.CREATED, "검색 기록을 생성했습니다."),
SEARCH_HISTORY_RETRIEVED(HttpStatus.OK, "검색 기록 조회에 성공했습니다."),
Expand Down
Loading
Loading