Compare commits
4 Commits
70d1795557
...
83028f7817
Author | SHA1 | Date |
---|---|---|
|
83028f7817 | |
|
5777d9700f | |
|
e1e9f4588a | |
|
be2f013b9a |
|
@ -11,6 +11,7 @@ import javax.persistence.Id
|
||||||
import javax.persistence.JoinColumn
|
import javax.persistence.JoinColumn
|
||||||
import javax.persistence.ManyToOne
|
import javax.persistence.ManyToOne
|
||||||
import javax.persistence.PrePersist
|
import javax.persistence.PrePersist
|
||||||
|
import javax.persistence.PreUpdate
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
class SeriesKeyword {
|
class SeriesKeyword {
|
||||||
|
@ -19,6 +20,7 @@ class SeriesKeyword {
|
||||||
var id: Long? = null
|
var id: Long? = null
|
||||||
|
|
||||||
var createdAt: LocalDateTime? = null
|
var createdAt: LocalDateTime? = null
|
||||||
|
var updatedAt: LocalDateTime? = null
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "series_id", nullable = false)
|
@JoinColumn(name = "series_id", nullable = false)
|
||||||
|
@ -28,8 +30,16 @@ class SeriesKeyword {
|
||||||
@JoinColumn(name = "keyword_id", nullable = false)
|
@JoinColumn(name = "keyword_id", nullable = false)
|
||||||
var keyword: HashTag? = null
|
var keyword: HashTag? = null
|
||||||
|
|
||||||
|
var isActive: Boolean = true
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
fun prePersist() {
|
fun prePersist() {
|
||||||
createdAt = LocalDateTime.now()
|
createdAt = LocalDateTime.now()
|
||||||
|
updatedAt = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
fun preUpdate() {
|
||||||
|
updatedAt = LocalDateTime.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package kr.co.vividnext.sodalive.marketing
|
package kr.co.vividnext.sodalive.marketing
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
@ -12,6 +13,7 @@ class AdTrackingController(private val service: AdTrackingService) {
|
||||||
fun trackingAppLaunch(
|
fun trackingAppLaunch(
|
||||||
@RequestBody request: AdTrackingAppLaunchRequest
|
@RequestBody request: AdTrackingAppLaunchRequest
|
||||||
) = run {
|
) = run {
|
||||||
|
ApiResponse.ok(
|
||||||
service.saveTrackingHistory(
|
service.saveTrackingHistory(
|
||||||
pid = request.pid,
|
pid = request.pid,
|
||||||
type = AdTrackingHistoryType.APP_LAUNCH,
|
type = AdTrackingHistoryType.APP_LAUNCH,
|
||||||
|
@ -19,5 +21,6 @@ class AdTrackingController(private val service: AdTrackingService) {
|
||||||
price = null,
|
price = null,
|
||||||
locale = null
|
locale = null
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package kr.co.vividnext.sodalive.search
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
|
import kr.co.vividnext.sodalive.content.ContentType
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/search")
|
||||||
|
class SearchController(private val service: SearchService) {
|
||||||
|
@GetMapping
|
||||||
|
fun searchUnified(
|
||||||
|
@RequestParam keyword: String,
|
||||||
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
|
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(
|
||||||
|
service.searchUnified(
|
||||||
|
keyword,
|
||||||
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
|
contentType = contentType ?: ContentType.ALL,
|
||||||
|
member = member
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/creators")
|
||||||
|
fun searchCreatorList(
|
||||||
|
@RequestParam keyword: String,
|
||||||
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
|
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||||
|
pageable: Pageable
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(
|
||||||
|
service.searchCreatorList(
|
||||||
|
keyword,
|
||||||
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
|
contentType = contentType ?: ContentType.ALL,
|
||||||
|
member = member,
|
||||||
|
offset = pageable.offset,
|
||||||
|
limit = pageable.pageSize.toLong()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/contents")
|
||||||
|
fun searchContentList(
|
||||||
|
@RequestParam keyword: String,
|
||||||
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
|
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||||
|
pageable: Pageable
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(
|
||||||
|
service.searchContentList(
|
||||||
|
keyword,
|
||||||
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
|
contentType = contentType ?: ContentType.ALL,
|
||||||
|
member = member,
|
||||||
|
offset = pageable.offset,
|
||||||
|
limit = pageable.pageSize.toLong()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/series")
|
||||||
|
fun searchSeriesList(
|
||||||
|
@RequestParam keyword: String,
|
||||||
|
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
|
||||||
|
@RequestParam("contentType", required = false) contentType: ContentType? = null,
|
||||||
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
|
||||||
|
pageable: Pageable
|
||||||
|
) = run {
|
||||||
|
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||||
|
ApiResponse.ok(
|
||||||
|
service.searchSeriesList(
|
||||||
|
keyword,
|
||||||
|
isAdultContentVisible = isAdultContentVisible ?: true,
|
||||||
|
contentType = contentType ?: ContentType.ALL,
|
||||||
|
member = member,
|
||||||
|
offset = pageable.offset,
|
||||||
|
limit = pageable.pageSize.toLong()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,354 @@
|
||||||
|
package kr.co.vividnext.sodalive.search
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.admin.content.series.genre.QSeriesGenre.seriesGenre
|
||||||
|
import kr.co.vividnext.sodalive.content.ContentType
|
||||||
|
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
||||||
|
import kr.co.vividnext.sodalive.content.hashtag.QAudioContentHashTag.audioContentHashTag
|
||||||
|
import kr.co.vividnext.sodalive.content.hashtag.QHashTag.hashTag
|
||||||
|
import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme
|
||||||
|
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
|
||||||
|
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent
|
||||||
|
import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.QSeriesKeyword.seriesKeyword
|
||||||
|
import kr.co.vividnext.sodalive.member.MemberRole
|
||||||
|
import kr.co.vividnext.sodalive.member.QMember.member
|
||||||
|
import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember
|
||||||
|
import kr.co.vividnext.sodalive.member.tag.QCreatorTag.creatorTag
|
||||||
|
import kr.co.vividnext.sodalive.member.tag.QMemberCreatorTag.memberCreatorTag
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
class SearchRepository(
|
||||||
|
private val queryFactory: JPAQueryFactory,
|
||||||
|
|
||||||
|
@Value("\${cloud.aws.cloud-front.host}")
|
||||||
|
private val imageHost: String
|
||||||
|
) {
|
||||||
|
fun searchCreatorTotalCount(
|
||||||
|
keyword: String,
|
||||||
|
memberId: Long
|
||||||
|
): Int {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(member.id)
|
||||||
|
.from(member)
|
||||||
|
.leftJoin(memberCreatorTag).on(memberCreatorTag.member.id.eq(member.id))
|
||||||
|
.leftJoin(creatorTag).on(memberCreatorTag.tag.id.eq(creatorTag.id))
|
||||||
|
.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
.where(
|
||||||
|
member.isActive.isTrue
|
||||||
|
.and(member.role.eq(MemberRole.CREATOR))
|
||||||
|
.and(creatorTag.isActive.isTrue.or(creatorTag.id.isNull))
|
||||||
|
.and(
|
||||||
|
member.nickname.containsIgnoreCase(keyword)
|
||||||
|
.or(member.introduce.containsIgnoreCase(keyword))
|
||||||
|
.or(creatorTag.tag.containsIgnoreCase(keyword))
|
||||||
|
)
|
||||||
|
.and(blockMember.id.isNull)
|
||||||
|
)
|
||||||
|
.groupBy(member.id)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchCreatorList(
|
||||||
|
keyword: String,
|
||||||
|
memberId: Long,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long
|
||||||
|
): List<SearchResponseItem> {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QSearchResponseItem(
|
||||||
|
member.id,
|
||||||
|
member.profileImage.prepend("/").prepend(imageHost),
|
||||||
|
member.nickname,
|
||||||
|
member.nickname
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(member)
|
||||||
|
.leftJoin(memberCreatorTag).on(memberCreatorTag.member.id.eq(member.id))
|
||||||
|
.leftJoin(creatorTag).on(memberCreatorTag.tag.id.eq(creatorTag.id))
|
||||||
|
.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
.where(
|
||||||
|
member.isActive.isTrue
|
||||||
|
.and(member.role.eq(MemberRole.CREATOR))
|
||||||
|
.and(creatorTag.isActive.isTrue.or(creatorTag.id.isNull))
|
||||||
|
.and(
|
||||||
|
member.nickname.containsIgnoreCase(keyword)
|
||||||
|
.or(member.introduce.containsIgnoreCase(keyword))
|
||||||
|
.or(creatorTag.tag.containsIgnoreCase(keyword))
|
||||||
|
)
|
||||||
|
.and(blockMember.id.isNull)
|
||||||
|
)
|
||||||
|
.groupBy(member.id)
|
||||||
|
.orderBy(member.id.desc())
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchContentTotalCount(
|
||||||
|
keyword: String,
|
||||||
|
memberId: Long,
|
||||||
|
isAdult: Boolean,
|
||||||
|
contentType: ContentType
|
||||||
|
): Int {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
|
||||||
|
var where = audioContent.member.isActive.isTrue
|
||||||
|
.and(audioContent.member.role.eq(MemberRole.CREATOR))
|
||||||
|
.and(audioContent.isActive.isTrue)
|
||||||
|
.and(audioContent.limited.isNull)
|
||||||
|
.and(audioContent.duration.isNotNull)
|
||||||
|
.and(
|
||||||
|
audioContent.title.containsIgnoreCase(keyword)
|
||||||
|
.or(audioContent.detail.containsIgnoreCase(keyword))
|
||||||
|
.or(audioContent.theme.theme.containsIgnoreCase(keyword))
|
||||||
|
.or(hashTag.tag.containsIgnoreCase(keyword).and(audioContentHashTag.isActive.isTrue))
|
||||||
|
|
||||||
|
)
|
||||||
|
.and(blockMember.id.isNull)
|
||||||
|
|
||||||
|
if (!isAdult) {
|
||||||
|
where = where.and(audioContent.isAdult.isFalse)
|
||||||
|
} else {
|
||||||
|
if (contentType != ContentType.ALL) {
|
||||||
|
where = where.and(
|
||||||
|
audioContent.member.isNull.or(
|
||||||
|
audioContent.member.auth.gender.eq(
|
||||||
|
if (contentType == ContentType.MALE) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(audioContent.id)
|
||||||
|
.from(audioContent)
|
||||||
|
.innerJoin(audioContent.member, member)
|
||||||
|
.leftJoin(audioContentTheme).on(audioContentTheme.id.eq(audioContent.theme.id))
|
||||||
|
.leftJoin(audioContentHashTag).on(audioContentHashTag.audioContent.id.eq(audioContent.id))
|
||||||
|
.leftJoin(hashTag).on(audioContentHashTag.hashTag.id.eq(hashTag.id))
|
||||||
|
.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
.where(where)
|
||||||
|
.groupBy(audioContent.id)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchContentList(
|
||||||
|
keyword: String,
|
||||||
|
memberId: Long,
|
||||||
|
isAdult: Boolean,
|
||||||
|
contentType: ContentType,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long
|
||||||
|
): List<SearchResponseItem> {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
|
||||||
|
var where = audioContent.member.isActive.isTrue
|
||||||
|
.and(audioContent.member.role.eq(MemberRole.CREATOR))
|
||||||
|
.and(audioContent.isActive.isTrue)
|
||||||
|
.and(audioContent.limited.isNull)
|
||||||
|
.and(audioContent.duration.isNotNull)
|
||||||
|
.and(
|
||||||
|
audioContent.title.containsIgnoreCase(keyword)
|
||||||
|
.or(audioContent.detail.containsIgnoreCase(keyword))
|
||||||
|
.or(audioContent.theme.theme.containsIgnoreCase(keyword))
|
||||||
|
.or(hashTag.tag.containsIgnoreCase(keyword).and(audioContentHashTag.isActive.isTrue))
|
||||||
|
|
||||||
|
)
|
||||||
|
.and(blockMember.id.isNull)
|
||||||
|
|
||||||
|
if (!isAdult) {
|
||||||
|
where = where.and(audioContent.isAdult.isFalse)
|
||||||
|
} else {
|
||||||
|
if (contentType != ContentType.ALL) {
|
||||||
|
where = where.and(
|
||||||
|
audioContent.member.isNull.or(
|
||||||
|
audioContent.member.auth.gender.eq(
|
||||||
|
if (contentType == ContentType.MALE) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QSearchResponseItem(
|
||||||
|
audioContent.id,
|
||||||
|
audioContent.coverImage.prepend("/").prepend(imageHost),
|
||||||
|
audioContent.title,
|
||||||
|
audioContent.member.nickname
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(audioContent)
|
||||||
|
.innerJoin(audioContent.member, member)
|
||||||
|
.leftJoin(audioContentTheme).on(audioContentTheme.id.eq(audioContent.theme.id))
|
||||||
|
.leftJoin(audioContentHashTag).on(audioContentHashTag.audioContent.id.eq(audioContent.id))
|
||||||
|
.leftJoin(hashTag).on(audioContentHashTag.hashTag.id.eq(hashTag.id))
|
||||||
|
.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
.where(where)
|
||||||
|
.groupBy(audioContent.id)
|
||||||
|
.orderBy(audioContent.id.desc())
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchSeriesTotalCount(
|
||||||
|
keyword: String,
|
||||||
|
memberId: Long,
|
||||||
|
isAdult: Boolean,
|
||||||
|
contentType: ContentType
|
||||||
|
): Int {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
|
||||||
|
var where = series.isActive.isTrue
|
||||||
|
.and(audioContent.isActive.isTrue)
|
||||||
|
.and(member.isActive.isTrue)
|
||||||
|
.and(member.isNotNull)
|
||||||
|
.and(member.role.eq(MemberRole.CREATOR))
|
||||||
|
.and(audioContent.duration.isNotNull)
|
||||||
|
.and(audioContent.limited.isNull)
|
||||||
|
.and(
|
||||||
|
series.title.containsIgnoreCase(keyword)
|
||||||
|
.or(series.introduction.containsIgnoreCase(keyword))
|
||||||
|
.or(seriesGenre.genre.containsIgnoreCase(keyword))
|
||||||
|
.or(hashTag.tag.containsIgnoreCase(keyword).and(seriesKeyword.isActive.isTrue))
|
||||||
|
)
|
||||||
|
.and(blockMember.id.isNull)
|
||||||
|
|
||||||
|
if (!isAdult) {
|
||||||
|
where = where
|
||||||
|
.and(series.isAdult.isFalse)
|
||||||
|
.and(audioContent.isAdult.isFalse)
|
||||||
|
} else {
|
||||||
|
if (contentType != ContentType.ALL) {
|
||||||
|
where = where.and(
|
||||||
|
audioContent.member.isNull.or(
|
||||||
|
audioContent.member.auth.gender.eq(
|
||||||
|
if (contentType == ContentType.MALE) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(series.id)
|
||||||
|
.from(seriesContent)
|
||||||
|
.innerJoin(seriesContent.series, series)
|
||||||
|
.innerJoin(seriesContent.content, audioContent)
|
||||||
|
.innerJoin(series.member, member)
|
||||||
|
.leftJoin(seriesGenre).on(seriesGenre.id.eq(series.genre.id))
|
||||||
|
.leftJoin(seriesKeyword).on(seriesKeyword.series.id.eq(series.id))
|
||||||
|
.leftJoin(hashTag).on(hashTag.id.eq(seriesKeyword.keyword.id))
|
||||||
|
.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
.where(where)
|
||||||
|
.groupBy(series.id, series.orders)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchSeriesList(
|
||||||
|
keyword: String,
|
||||||
|
memberId: Long,
|
||||||
|
isAdult: Boolean,
|
||||||
|
contentType: ContentType,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long
|
||||||
|
): List<SearchResponseItem> {
|
||||||
|
val blockMemberCondition = blockMember.member.id.eq(member.id)
|
||||||
|
.and(blockMember.isActive.isTrue)
|
||||||
|
.and(blockMember.blockedMember.id.eq(memberId))
|
||||||
|
|
||||||
|
var where = series.isActive.isTrue
|
||||||
|
.and(audioContent.isActive.isTrue)
|
||||||
|
.and(member.isActive.isTrue)
|
||||||
|
.and(member.isNotNull)
|
||||||
|
.and(member.role.eq(MemberRole.CREATOR))
|
||||||
|
.and(audioContent.duration.isNotNull)
|
||||||
|
.and(audioContent.limited.isNull)
|
||||||
|
.and(
|
||||||
|
series.title.containsIgnoreCase(keyword)
|
||||||
|
.or(series.introduction.containsIgnoreCase(keyword))
|
||||||
|
.or(seriesGenre.genre.containsIgnoreCase(keyword))
|
||||||
|
.or(hashTag.tag.containsIgnoreCase(keyword).and(seriesKeyword.isActive.isTrue))
|
||||||
|
)
|
||||||
|
.and(blockMember.id.isNull)
|
||||||
|
|
||||||
|
if (!isAdult) {
|
||||||
|
where = where
|
||||||
|
.and(series.isAdult.isFalse)
|
||||||
|
.and(audioContent.isAdult.isFalse)
|
||||||
|
} else {
|
||||||
|
if (contentType != ContentType.ALL) {
|
||||||
|
where = where.and(
|
||||||
|
audioContent.member.isNull.or(
|
||||||
|
audioContent.member.auth.gender.eq(
|
||||||
|
if (contentType == ContentType.MALE) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryFactory
|
||||||
|
.select(
|
||||||
|
QSearchResponseItem(
|
||||||
|
series.id,
|
||||||
|
series.coverImage.prepend("/").prepend(imageHost),
|
||||||
|
series.title,
|
||||||
|
series.member.nickname
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.from(seriesContent)
|
||||||
|
.innerJoin(seriesContent.series, series)
|
||||||
|
.innerJoin(seriesContent.content, audioContent)
|
||||||
|
.innerJoin(series.member, member)
|
||||||
|
.leftJoin(seriesGenre).on(seriesGenre.id.eq(series.genre.id))
|
||||||
|
.leftJoin(seriesKeyword).on(seriesKeyword.series.id.eq(series.id))
|
||||||
|
.leftJoin(hashTag).on(hashTag.id.eq(seriesKeyword.keyword.id))
|
||||||
|
.leftJoin(blockMember).on(blockMemberCondition)
|
||||||
|
.where(where)
|
||||||
|
.groupBy(series.id, series.orders)
|
||||||
|
.orderBy(series.orders.asc())
|
||||||
|
.offset(offset)
|
||||||
|
.limit(limit)
|
||||||
|
.fetch()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package kr.co.vividnext.sodalive.search
|
||||||
|
|
||||||
|
import com.querydsl.core.annotations.QueryProjection
|
||||||
|
|
||||||
|
data class SearchUnifiedResponse(
|
||||||
|
val creatorList: List<SearchResponseItem>,
|
||||||
|
val contentList: List<SearchResponseItem>,
|
||||||
|
val seriesList: List<SearchResponseItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SearchResponse(
|
||||||
|
val totalCount: Int,
|
||||||
|
val items: List<SearchResponseItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SearchResponseItem @QueryProjection constructor(
|
||||||
|
val id: Long,
|
||||||
|
val imageUrl: String,
|
||||||
|
val title: String,
|
||||||
|
val nickname: String
|
||||||
|
) {
|
||||||
|
var type: SearchResponseType = SearchResponseType.CREATOR
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SearchResponseType {
|
||||||
|
CREATOR, CONTENT, SERIES
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package kr.co.vividnext.sodalive.search
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.content.ContentType
|
||||||
|
import kr.co.vividnext.sodalive.member.Member
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class SearchService(private val repository: SearchRepository) {
|
||||||
|
fun searchUnified(
|
||||||
|
keyword: String,
|
||||||
|
isAdultContentVisible: Boolean,
|
||||||
|
contentType: ContentType,
|
||||||
|
member: Member
|
||||||
|
): SearchUnifiedResponse {
|
||||||
|
val isAdult = member.auth != null && isAdultContentVisible
|
||||||
|
|
||||||
|
val creatorList = repository.searchCreatorList(
|
||||||
|
keyword = keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
offset = 0,
|
||||||
|
limit = 3
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
it.type = SearchResponseType.CREATOR
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
val contentList = repository.searchContentList(
|
||||||
|
keyword = keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdult = isAdult,
|
||||||
|
contentType = contentType,
|
||||||
|
offset = 0,
|
||||||
|
limit = 3
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
it.type = SearchResponseType.CONTENT
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
val seriesList = repository.searchSeriesList(
|
||||||
|
keyword = keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdult = isAdult,
|
||||||
|
contentType = contentType,
|
||||||
|
offset = 0,
|
||||||
|
limit = 3
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
it.type = SearchResponseType.SERIES
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchUnifiedResponse(
|
||||||
|
creatorList = creatorList,
|
||||||
|
contentList = contentList,
|
||||||
|
seriesList = seriesList
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchCreatorList(
|
||||||
|
keyword: String,
|
||||||
|
isAdultContentVisible: Boolean,
|
||||||
|
contentType: ContentType,
|
||||||
|
member: Member,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long
|
||||||
|
): SearchResponse {
|
||||||
|
val totalCount = repository.searchCreatorTotalCount(keyword, memberId = member.id!!)
|
||||||
|
val items = repository.searchCreatorList(
|
||||||
|
keyword = keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
offset = offset,
|
||||||
|
limit = limit
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
it.type = SearchResponseType.CREATOR
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchResponse(totalCount, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchContentList(
|
||||||
|
keyword: String,
|
||||||
|
isAdultContentVisible: Boolean,
|
||||||
|
contentType: ContentType,
|
||||||
|
member: Member,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long
|
||||||
|
): SearchResponse {
|
||||||
|
val isAdult = member.auth != null && isAdultContentVisible
|
||||||
|
|
||||||
|
val totalCount = repository.searchContentTotalCount(
|
||||||
|
keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdult = isAdult,
|
||||||
|
contentType = contentType
|
||||||
|
)
|
||||||
|
|
||||||
|
val items = repository.searchContentList(
|
||||||
|
keyword = keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdult = isAdult,
|
||||||
|
contentType = contentType,
|
||||||
|
offset = offset,
|
||||||
|
limit = limit
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
it.type = SearchResponseType.CONTENT
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchResponse(totalCount, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun searchSeriesList(
|
||||||
|
keyword: String,
|
||||||
|
isAdultContentVisible: Boolean,
|
||||||
|
contentType: ContentType,
|
||||||
|
member: Member,
|
||||||
|
offset: Long,
|
||||||
|
limit: Long
|
||||||
|
): SearchResponse {
|
||||||
|
val isAdult = member.auth != null && isAdultContentVisible
|
||||||
|
|
||||||
|
val totalCount = repository.searchSeriesTotalCount(
|
||||||
|
keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdult = isAdult,
|
||||||
|
contentType = contentType
|
||||||
|
)
|
||||||
|
|
||||||
|
val items = repository.searchSeriesList(
|
||||||
|
keyword = keyword,
|
||||||
|
memberId = member.id!!,
|
||||||
|
isAdult = isAdult,
|
||||||
|
contentType = contentType,
|
||||||
|
offset = offset,
|
||||||
|
limit = limit
|
||||||
|
)
|
||||||
|
.map {
|
||||||
|
it.type = SearchResponseType.SERIES
|
||||||
|
it
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchResponse(totalCount, items)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue