feat: 블로그 핵심 기능 구현 (댓글, 태그, 카테고리, 이미지)
[댓글 시스템] - 계층형(대댓글) 구조 및 회원/비회원 하이브리드 작성 로직 구현 - 비회원 댓글용 비밀번호 암호화 저장 (PasswordEncoder 적용) - 관리자용 댓글 강제 삭제 및 전체 댓글 모니터링(Dashboard) API 구현 [태그 & 카테고리] - N:M 태그 시스템(PostTag) 엔티티 설계 및 게시글 작성 시 자동 저장 로직 - 계층형(Tree) 카테고리 구조 구현 및 관리자 생성/삭제 API - QueryDSL 검색 조건에 태그 및 카테고리 필터링 추가 [이미지 업로드] - AWS S3 (MinIO) 연동 및 Bucket 자동 생성/Public 정책 설정 - 마크다운 에디터용 이미지 업로드 API 구현
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package me.wypark.blogbackend.domain.post
|
||||
|
||||
import me.wypark.blogbackend.api.dto.PostResponse
|
||||
import me.wypark.blogbackend.api.dto.PostSaveRequest
|
||||
import me.wypark.blogbackend.api.dto.PostSummaryResponse
|
||||
import me.wypark.blogbackend.domain.category.CategoryRepository
|
||||
import me.wypark.blogbackend.domain.tag.PostTag
|
||||
import me.wypark.blogbackend.domain.tag.Tag
|
||||
import me.wypark.blogbackend.domain.tag.TagRepository
|
||||
import me.wypark.blogbackend.domain.user.MemberRepository
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
class PostService(
|
||||
private val postRepository: PostRepository,
|
||||
private val categoryRepository: CategoryRepository,
|
||||
private val memberRepository: MemberRepository,
|
||||
private val tagRepository: TagRepository
|
||||
) {
|
||||
|
||||
/**
|
||||
* [Public] 전체 게시글 목록 조회 (페이징)
|
||||
*/
|
||||
fun getPosts(pageable: Pageable): Page<PostSummaryResponse> {
|
||||
return postRepository.findAll(pageable)
|
||||
.map { PostSummaryResponse.from(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* [Public] 게시글 상세 조회 (Slug 기반) + 조회수 증가
|
||||
*/
|
||||
@Transactional
|
||||
fun getPostBySlug(slug: String): PostResponse {
|
||||
val post = postRepository.findBySlug(slug)
|
||||
?: throw IllegalArgumentException("해당 게시글을 찾을 수 없습니다: $slug")
|
||||
|
||||
post.increaseViewCount() // 조회수 1 증가 (Dirty Checking)
|
||||
|
||||
return PostResponse.from(post)
|
||||
}
|
||||
|
||||
/**
|
||||
* [Admin] 게시글 작성
|
||||
*/
|
||||
@Transactional
|
||||
fun createPost(request: PostSaveRequest, email: String): Long {
|
||||
if (postRepository.existsBySlug(request.slug)) { throw IllegalArgumentException("이미 존재하는 Slug입니다.") }
|
||||
val member = memberRepository.findByEmail(email) ?: throw IllegalArgumentException("회원 없음")
|
||||
val category = request.categoryId?.let { categoryRepository.findByIdOrNull(it) }
|
||||
|
||||
val post = Post(
|
||||
title = request.title,
|
||||
content = request.content,
|
||||
slug = request.slug,
|
||||
member = member,
|
||||
category = category
|
||||
)
|
||||
|
||||
val postTags = request.tags.map { tagName ->
|
||||
val tag = tagRepository.findByName(tagName)
|
||||
?: tagRepository.save(Tag(name = tagName))
|
||||
|
||||
PostTag(post = post, tag = tag)
|
||||
}
|
||||
|
||||
post.addTags(postTags)
|
||||
|
||||
return postRepository.save(post).id!!
|
||||
}
|
||||
|
||||
fun searchPosts(keyword: String?, categoryName: String?, tagName: String?, pageable: Pageable): Page<PostSummaryResponse> {
|
||||
return postRepository.search(keyword, categoryName, tagName, pageable)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user