feat(ai-character): 커뮤니티 게시글 관리자 API를 추가한다
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package kr.co.vividnext.sodalive.v2.api.admin.aicharacter.community
|
||||
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.v2.api.admin.aicharacter.error.AiCharacterAdminApiException
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException
|
||||
import org.springframework.web.bind.annotation.DeleteMapping
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.PutMapping
|
||||
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.RequestPart
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v2/admin/ai-characters/{characterId:[0-9]+}/community-posts")
|
||||
class AiCharacterAdminCommunityPostController(
|
||||
private val facade: AiCharacterAdminCommunityPostFacade
|
||||
) {
|
||||
@GetMapping
|
||||
fun list(
|
||||
@PathVariable characterId: Long,
|
||||
@RequestParam(defaultValue = "0") page: Int,
|
||||
@RequestParam(defaultValue = "20") size: Int
|
||||
): ApiResponse<AiCharacterAdminCommunityPostListResponse> {
|
||||
return ApiResponse.ok(facade.list(characterId, page, size))
|
||||
}
|
||||
|
||||
@PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
fun create(
|
||||
@PathVariable characterId: Long,
|
||||
@RequestPart(value = "audioFile", required = false) audioFile: MultipartFile?,
|
||||
@RequestPart(value = "postImage", required = false) postImage: MultipartFile?,
|
||||
@RequestPart("request") request: String,
|
||||
multipartRequest: MultipartHttpServletRequest
|
||||
): ApiResponse<Nothing> {
|
||||
requireAllowedMultipartParts(multipartRequest, CREATE_MULTIPART_PARTS)
|
||||
requireJsonRequestPart(multipartRequest)
|
||||
facade.create(characterId, audioFile, postImage, request)
|
||||
return ApiResponse.ok(null)
|
||||
}
|
||||
|
||||
@PutMapping("/{postId:[0-9]+}", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
fun update(
|
||||
@PathVariable characterId: Long,
|
||||
@PathVariable postId: Long,
|
||||
@RequestPart(value = "postImage", required = false) postImage: MultipartFile?,
|
||||
@RequestPart("request") request: String,
|
||||
multipartRequest: MultipartHttpServletRequest
|
||||
): ApiResponse<Nothing> {
|
||||
requireAllowedMultipartParts(multipartRequest, UPDATE_MULTIPART_PARTS)
|
||||
requireJsonRequestPart(multipartRequest)
|
||||
facade.update(characterId, postId, postImage, request)
|
||||
return ApiResponse.ok(null)
|
||||
}
|
||||
|
||||
private fun requireAllowedMultipartParts(
|
||||
multipartRequest: MultipartHttpServletRequest,
|
||||
allowedParts: Set<String>
|
||||
) {
|
||||
if (multipartRequest.fileMap.keys.any { it !in allowedParts } ||
|
||||
multipartRequest.parts.any { it.name !in allowedParts }
|
||||
) {
|
||||
throw AiCharacterAdminApiException(HttpStatus.BAD_REQUEST, "common.error.invalid_request")
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireJsonRequestPart(multipartRequest: MultipartHttpServletRequest) {
|
||||
val contentType = multipartRequest.getMultipartHeaders("request")?.contentType
|
||||
?: multipartRequest.getPart("request")?.contentType?.let(MediaType::parseMediaType)
|
||||
if (contentType == null || !MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
|
||||
throw HttpMediaTypeNotSupportedException(contentType, listOf(MediaType.APPLICATION_JSON))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val CREATE_MULTIPART_PARTS = setOf("audioFile", "postImage", "request")
|
||||
private val UPDATE_MULTIPART_PARTS = setOf("postImage", "request")
|
||||
}
|
||||
|
||||
@GetMapping("/{postId:[0-9]+}/comments")
|
||||
fun comments(
|
||||
@PathVariable characterId: Long,
|
||||
@PathVariable postId: Long,
|
||||
@RequestParam(defaultValue = "0") page: Int,
|
||||
@RequestParam(defaultValue = "20") size: Int
|
||||
): ApiResponse<AiCharacterAdminCommunityPostCommentListResponse> {
|
||||
return ApiResponse.ok(facade.comments(characterId, postId, page, size))
|
||||
}
|
||||
|
||||
@PostMapping("/{postId:[0-9]+}/comments", consumes = [MediaType.APPLICATION_JSON_VALUE])
|
||||
fun createComment(
|
||||
@PathVariable characterId: Long,
|
||||
@PathVariable postId: Long,
|
||||
@RequestBody request: String
|
||||
): ApiResponse<Nothing> {
|
||||
facade.createComment(characterId, postId, request)
|
||||
return ApiResponse.ok(null)
|
||||
}
|
||||
|
||||
@PutMapping("/{postId:[0-9]+}/comments/{commentId:[0-9]+}", consumes = [MediaType.APPLICATION_JSON_VALUE])
|
||||
fun updateComment(
|
||||
@PathVariable characterId: Long,
|
||||
@PathVariable postId: Long,
|
||||
@PathVariable commentId: Long,
|
||||
@RequestBody request: String
|
||||
): ApiResponse<Nothing> {
|
||||
facade.updateComment(characterId, postId, commentId, request)
|
||||
return ApiResponse.ok(null)
|
||||
}
|
||||
|
||||
@DeleteMapping("/{postId:[0-9]+}/comments/{commentId:[0-9]+}")
|
||||
fun deleteComment(
|
||||
@PathVariable characterId: Long,
|
||||
@PathVariable postId: Long,
|
||||
@PathVariable commentId: Long
|
||||
): ApiResponse<Nothing> {
|
||||
facade.deleteComment(characterId, postId, commentId)
|
||||
return ApiResponse.ok(null)
|
||||
}
|
||||
|
||||
@GetMapping("/{postId:[0-9]+}/comments/{commentId:[0-9]+}/replies")
|
||||
fun commentReplies(
|
||||
@PathVariable characterId: Long,
|
||||
@PathVariable postId: Long,
|
||||
@PathVariable commentId: Long,
|
||||
@RequestParam(defaultValue = "0") page: Int,
|
||||
@RequestParam(defaultValue = "20") size: Int
|
||||
): ApiResponse<AiCharacterAdminCommunityPostCommentListResponse> {
|
||||
return ApiResponse.ok(facade.commentReplies(characterId, postId, commentId, page, size))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package kr.co.vividnext.sodalive.v2.api.admin.aicharacter.community
|
||||
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.GetCommunityPostListResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.GetCommunityPostCommentListResponse
|
||||
|
||||
typealias AiCharacterAdminCommunityPostDto = GetCommunityPostListResponse
|
||||
typealias AiCharacterAdminCommunityPostCommentListResponse = GetCommunityPostCommentListResponse
|
||||
|
||||
data class AiCharacterAdminCommunityPostListResponse(
|
||||
val totalCount: Long,
|
||||
val page: Int,
|
||||
val size: Int,
|
||||
val hasNext: Boolean,
|
||||
val items: List<AiCharacterAdminCommunityPostDto>
|
||||
)
|
||||
|
||||
data class AiCharacterAdminCommunityPostUpdateRequest(
|
||||
val content: String? = null,
|
||||
val isCommentAvailable: Boolean? = null,
|
||||
val isAdult: Boolean? = null,
|
||||
val isActive: Boolean? = null,
|
||||
val isFixed: Boolean? = null
|
||||
)
|
||||
|
||||
data class AiCharacterAdminCommunityPostCommentCreateRequest(
|
||||
val comment: String,
|
||||
val parentId: Long? = null,
|
||||
val isSecret: Boolean = false
|
||||
)
|
||||
|
||||
data class AiCharacterAdminCommunityPostCommentUpdateRequest(
|
||||
val comment: String
|
||||
)
|
||||
@@ -0,0 +1,286 @@
|
||||
package kr.co.vividnext.sodalive.v2.api.admin.aicharacter.community
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreateCommunityPostRequest
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityService
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.ModifyCommunityPostRequest
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.UpdateCommunityPostFixedRequest
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.CreatorCommunityCommentRepository
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.GetCommunityPostCommentListItem
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.ModifyCommunityPostCommentRequest
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCommunityLikeRepository
|
||||
import kr.co.vividnext.sodalive.extensions.getTimeAgoString
|
||||
import kr.co.vividnext.sodalive.extensions.toUtcIso
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import kr.co.vividnext.sodalive.v2.api.admin.aicharacter.application.AiCharacterAdminTarget
|
||||
import kr.co.vividnext.sodalive.v2.api.admin.aicharacter.application.AiCharacterAdminTargetResolver
|
||||
import kr.co.vividnext.sodalive.v2.api.admin.aicharacter.error.AiCharacterAdminApiException
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.time.ZoneId
|
||||
|
||||
@Service
|
||||
class AiCharacterAdminCommunityPostFacade(
|
||||
private val targetResolver: AiCharacterAdminTargetResolver,
|
||||
private val legacyService: CreatorCommunityService,
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val repository: AiCharacterAdminCommunityPostRepository,
|
||||
private val memberRepository: MemberRepository,
|
||||
private val likeRepository: CreatorCommunityLikeRepository,
|
||||
private val commentRepository: CreatorCommunityCommentRepository,
|
||||
private val audioContentCloudFront: AudioContentCloudFront,
|
||||
@Value("\${cloud.aws.cloud-front.host}") private val imageHost: String
|
||||
) {
|
||||
@Transactional
|
||||
fun create(characterId: Long, audioFile: MultipartFile?, postImage: MultipartFile?, request: String) {
|
||||
val target = resolveActiveTarget(characterId)
|
||||
readRequest(request, CreateCommunityPostRequest::class.java)
|
||||
legacyService.createCommunityPost(audioFile, postImage, request, target.creatorMember)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun update(characterId: Long, postId: Long, postImage: MultipartFile?, requestString: String) {
|
||||
val target = resolveActiveTarget(characterId)
|
||||
val creatorMember = target.creatorMember
|
||||
val creatorMemberId = creatorMember.id ?: throw invalidRequest()
|
||||
repository.findActiveByIdAndCreatorMemberId(postId, creatorMemberId) ?: throw invalidRequest()
|
||||
|
||||
rejectExplicitNull(requestString, "isFixed")
|
||||
val request = readRequest(requestString, AiCharacterAdminCommunityPostUpdateRequest::class.java)
|
||||
val modifyRequest = ModifyCommunityPostRequest(
|
||||
creatorCommunityId = postId,
|
||||
content = request.content,
|
||||
isCommentAvailable = request.isCommentAvailable,
|
||||
isAdult = request.isAdult,
|
||||
isActive = request.isActive
|
||||
)
|
||||
|
||||
if (request.isActive != false && request.isFixed != null) {
|
||||
memberRepository.findByIdForUpdate(creatorMemberId) ?: throw invalidRequest()
|
||||
legacyService.updateCommunityPostFixed(
|
||||
UpdateCommunityPostFixedRequest(postId = postId, isFixed = request.isFixed),
|
||||
creatorMember
|
||||
)
|
||||
}
|
||||
|
||||
legacyService.modifyCommunityPost(postImage, objectMapper.writeValueAsString(modifyRequest), creatorMember)
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun list(
|
||||
characterId: Long,
|
||||
page: Int,
|
||||
size: Int
|
||||
): AiCharacterAdminCommunityPostListResponse {
|
||||
val target = resolveActiveTarget(characterId)
|
||||
if (page < 0 || size < 1) throw invalidRequest()
|
||||
|
||||
val creatorMemberId = target.creatorMember.id ?: throw invalidRequest()
|
||||
val pageable = PageRequest.of(page, size)
|
||||
val items = repository.findActiveByCreatorMemberId(creatorMemberId, pageable)
|
||||
.map { post -> post.toResponse(target.creatorMember, creatorMemberId) }
|
||||
val totalCount = repository.countActiveByCreatorMemberId(creatorMemberId)
|
||||
|
||||
return AiCharacterAdminCommunityPostListResponse(
|
||||
totalCount = totalCount,
|
||||
page = page,
|
||||
size = size,
|
||||
hasNext = (page + 1L) * size < totalCount,
|
||||
items = items
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun comments(
|
||||
characterId: Long,
|
||||
postId: Long,
|
||||
page: Int,
|
||||
size: Int
|
||||
): AiCharacterAdminCommunityPostCommentListResponse {
|
||||
val target = resolveOwnedActivePost(characterId, postId)
|
||||
validateListRequest(page, size)
|
||||
val pageable = PageRequest.of(page, size)
|
||||
return utcCommentDates(
|
||||
legacyService.getCommunityPostCommentList(
|
||||
postId = postId,
|
||||
memberId = target.creatorMember.id ?: throw invalidRequest(),
|
||||
timezone = UTC_TIMEZONE,
|
||||
offset = pageable.offset,
|
||||
limit = pageable.pageSize.toLong(),
|
||||
isAdult = true
|
||||
),
|
||||
postId
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun commentReplies(
|
||||
characterId: Long,
|
||||
postId: Long,
|
||||
commentId: Long,
|
||||
page: Int,
|
||||
size: Int
|
||||
): AiCharacterAdminCommunityPostCommentListResponse {
|
||||
val target = resolveOwnedActivePost(characterId, postId)
|
||||
validateListRequest(page, size)
|
||||
repository.findActiveRootCommentByIdAndPostId(commentId, postId) ?: throw invalidRequest()
|
||||
val pageable = PageRequest.of(page, size)
|
||||
return utcCommentDates(
|
||||
legacyService.getCommentReplyList(
|
||||
commentId = commentId,
|
||||
memberId = target.creatorMember.id ?: throw invalidRequest(),
|
||||
timezone = UTC_TIMEZONE,
|
||||
offset = pageable.offset,
|
||||
limit = pageable.pageSize.toLong(),
|
||||
isAdult = true
|
||||
),
|
||||
postId
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun createComment(characterId: Long, postId: Long, requestString: String) {
|
||||
val target = resolveOwnedActivePost(characterId, postId)
|
||||
val request = readRequest(requestString, AiCharacterAdminCommunityPostCommentCreateRequest::class.java)
|
||||
request.parentId?.let { parentId ->
|
||||
repository.findActiveRootCommentByIdAndPostId(parentId, postId) ?: throw invalidRequest()
|
||||
}
|
||||
legacyService.createCommunityPostComment(
|
||||
member = target.creatorMember,
|
||||
comment = request.comment,
|
||||
postId = postId,
|
||||
parentId = request.parentId,
|
||||
isSecret = request.isSecret,
|
||||
isAdult = true
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun updateComment(characterId: Long, postId: Long, commentId: Long, requestString: String) {
|
||||
val target = resolveOwnedActivePost(characterId, postId)
|
||||
val request = readRequest(requestString, AiCharacterAdminCommunityPostCommentUpdateRequest::class.java)
|
||||
val comment = repository.findCommentByIdAndPostId(commentId, postId) ?: throw invalidRequest()
|
||||
if (!comment.isActive || comment.member?.id != target.creatorMember.id) throw invalidRequest()
|
||||
legacyService.modifyCommunityPostComment(
|
||||
ModifyCommunityPostCommentRequest(commentId = commentId, comment = request.comment, isActive = null),
|
||||
target.creatorMember
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun deleteComment(characterId: Long, postId: Long, commentId: Long) {
|
||||
val target = resolveOwnedActivePost(characterId, postId)
|
||||
val comment = repository.findCommentByIdAndPostId(commentId, postId) ?: throw invalidRequest()
|
||||
if (!comment.isActive) return
|
||||
legacyService.modifyCommunityPostComment(
|
||||
ModifyCommunityPostCommentRequest(commentId = commentId, comment = null, isActive = false),
|
||||
target.creatorMember
|
||||
)
|
||||
}
|
||||
|
||||
private fun CreatorCommunity.toResponse(member: Member, creatorMemberId: Long): AiCharacterAdminCommunityPostDto {
|
||||
val postId = id ?: throw invalidRequest()
|
||||
val createdAt = createdAt ?: throw invalidRequest()
|
||||
val audioUrl = audioPath?.let {
|
||||
audioContentCloudFront.generateSignedURL(it, AUDIO_URL_EXPIRATION_MILLIS)
|
||||
}
|
||||
val commentCount = if (isCommentAvailable) {
|
||||
commentRepository.totalCountCommentByPostId(
|
||||
postId = postId,
|
||||
memberId = creatorMemberId,
|
||||
isContentCreator = true
|
||||
)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
return AiCharacterAdminCommunityPostDto(
|
||||
postId = postId,
|
||||
creatorId = creatorMemberId,
|
||||
creatorNickname = member.nickname,
|
||||
creatorProfileUrl = "$imageHost/${member.profileImage ?: DEFAULT_PROFILE_IMAGE_PATH}",
|
||||
imageUrl = imagePath?.let { "$imageHost/$it" },
|
||||
audioUrl = audioUrl,
|
||||
content = content,
|
||||
price = price,
|
||||
date = createdAt.getTimeAgoString(),
|
||||
dateUtc = createdAt.atZone(ZoneId.of("UTC")).toInstant().toString(),
|
||||
isCommentAvailable = isCommentAvailable,
|
||||
isAdult = isAdult,
|
||||
isFixed = isFixed,
|
||||
isLike = false,
|
||||
existOrdered = true,
|
||||
likeCount = likeRepository.totalCountCommunityPostLikeByPostId(postId),
|
||||
commentCount = commentCount,
|
||||
firstComment = null
|
||||
)
|
||||
}
|
||||
|
||||
private fun resolveActiveTarget(characterId: Long): AiCharacterAdminTarget {
|
||||
val target = targetResolver.resolve(characterId)
|
||||
if (!target.chatCharacter.isActive) throw invalidRequest()
|
||||
return target
|
||||
}
|
||||
|
||||
private fun resolveOwnedActivePost(characterId: Long, postId: Long): AiCharacterAdminTarget {
|
||||
val target = resolveActiveTarget(characterId)
|
||||
val creatorMemberId = target.creatorMember.id ?: throw invalidRequest()
|
||||
repository.findActiveByIdAndCreatorMemberId(postId, creatorMemberId) ?: throw invalidRequest()
|
||||
return target
|
||||
}
|
||||
|
||||
private fun validateListRequest(page: Int, size: Int) {
|
||||
if (page < 0 || size < 1) throw invalidRequest()
|
||||
}
|
||||
|
||||
private fun utcCommentDates(
|
||||
response: AiCharacterAdminCommunityPostCommentListResponse,
|
||||
postId: Long
|
||||
): AiCharacterAdminCommunityPostCommentListResponse {
|
||||
return response.copy(items = response.items.map { it.withUtcDate(postId) })
|
||||
}
|
||||
|
||||
private fun GetCommunityPostCommentListItem.withUtcDate(postId: Long): GetCommunityPostCommentListItem {
|
||||
val createdAt = repository.findCommentByIdAndPostId(id, postId)?.createdAt ?: throw invalidRequest()
|
||||
return copy(date = createdAt.toUtcIso())
|
||||
}
|
||||
|
||||
private fun <T> readRequest(requestString: String, requestClass: Class<T>): T {
|
||||
return try {
|
||||
objectMapper.readerFor(requestClass)
|
||||
.with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.with(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
|
||||
.with(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)
|
||||
.readValue(requestString)
|
||||
} catch (_: JsonProcessingException) {
|
||||
throw invalidRequest()
|
||||
}
|
||||
}
|
||||
|
||||
private fun rejectExplicitNull(requestString: String, fieldName: String) {
|
||||
try {
|
||||
if (objectMapper.readTree(requestString).path(fieldName).isNull) throw invalidRequest()
|
||||
} catch (_: JsonProcessingException) {
|
||||
throw invalidRequest()
|
||||
}
|
||||
}
|
||||
|
||||
private fun invalidRequest(): AiCharacterAdminApiException {
|
||||
return AiCharacterAdminApiException(HttpStatus.BAD_REQUEST, "common.error.invalid_request")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val AUDIO_URL_EXPIRATION_MILLIS = 1_800_000L
|
||||
const val DEFAULT_PROFILE_IMAGE_PATH = "profile/default_profile.png"
|
||||
const val UTC_TIMEZONE = "UTC"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package kr.co.vividnext.sodalive.v2.api.admin.aicharacter.community
|
||||
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunity
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.QCreatorCommunity.creatorCommunity
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.CreatorCommunityComment
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.comment.QCreatorCommunityComment.creatorCommunityComment
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
@Repository
|
||||
class AiCharacterAdminCommunityPostRepository(
|
||||
private val queryFactory: JPAQueryFactory
|
||||
) {
|
||||
fun findActiveByCreatorMemberId(creatorMemberId: Long, pageable: Pageable): List<CreatorCommunity> {
|
||||
return queryFactory
|
||||
.selectFrom(creatorCommunity)
|
||||
.where(
|
||||
creatorCommunity.member.id.eq(creatorMemberId)
|
||||
.and(creatorCommunity.isActive.isTrue)
|
||||
)
|
||||
.offset(pageable.offset)
|
||||
.limit(pageable.pageSize.toLong())
|
||||
.orderBy(
|
||||
creatorCommunity.isFixed.desc(),
|
||||
creatorCommunity.fixedAt.desc().nullsLast(),
|
||||
creatorCommunity.createdAt.desc()
|
||||
)
|
||||
.fetch()
|
||||
}
|
||||
|
||||
fun countActiveByCreatorMemberId(creatorMemberId: Long): Long {
|
||||
return queryFactory
|
||||
.select(creatorCommunity.count())
|
||||
.from(creatorCommunity)
|
||||
.where(
|
||||
creatorCommunity.member.id.eq(creatorMemberId)
|
||||
.and(creatorCommunity.isActive.isTrue)
|
||||
)
|
||||
.fetchOne() ?: 0L
|
||||
}
|
||||
|
||||
fun findActiveByIdAndCreatorMemberId(postId: Long, creatorMemberId: Long): CreatorCommunity? {
|
||||
return queryFactory
|
||||
.selectFrom(creatorCommunity)
|
||||
.where(
|
||||
creatorCommunity.id.eq(postId)
|
||||
.and(creatorCommunity.member.id.eq(creatorMemberId))
|
||||
.and(creatorCommunity.isActive.isTrue)
|
||||
)
|
||||
.fetchOne()
|
||||
}
|
||||
|
||||
fun findCommentByIdAndPostId(commentId: Long, postId: Long): CreatorCommunityComment? {
|
||||
return queryFactory
|
||||
.selectFrom(creatorCommunityComment)
|
||||
.where(
|
||||
creatorCommunityComment.id.eq(commentId)
|
||||
.and(creatorCommunityComment.creatorCommunity.id.eq(postId))
|
||||
)
|
||||
.fetchOne()
|
||||
}
|
||||
|
||||
fun findActiveRootCommentByIdAndPostId(commentId: Long, postId: Long): CreatorCommunityComment? {
|
||||
return queryFactory
|
||||
.selectFrom(creatorCommunityComment)
|
||||
.where(
|
||||
creatorCommunityComment.id.eq(commentId)
|
||||
.and(creatorCommunityComment.creatorCommunity.id.eq(postId))
|
||||
.and(creatorCommunityComment.parent.isNull)
|
||||
.and(creatorCommunityComment.isActive.isTrue)
|
||||
)
|
||||
.fetchOne()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user