관리자 - FAQ API

This commit is contained in:
Klaus 2023-08-06 22:10:33 +09:00
parent 87a5ceee9c
commit a983595bad
9 changed files with 234 additions and 0 deletions

View File

@ -0,0 +1,7 @@
package kr.co.vividnext.sodalive.faq
data class CreateFaqRequest(
val question: String,
val answer: String,
val category: String
)

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.faq
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
@Entity
data class Faq(
@Column(nullable = false)
var question: String,
@Column(columnDefinition = "TEXT", nullable = false)
var answer: String,
var isActive: Boolean = true,
val orders: Int = 1
) : BaseEntity() {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", nullable = false)
var category: FaqCategory? = null
}

View File

@ -0,0 +1,13 @@
package kr.co.vividnext.sodalive.faq
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Column
import javax.persistence.Entity
@Entity
data class FaqCategory(
@Column(nullable = false)
val category: String,
val isActive: Boolean = true,
val orders: Int = 1
) : BaseEntity()

View File

@ -0,0 +1,7 @@
package kr.co.vividnext.sodalive.faq
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface FaqCategoryRepository : JpaRepository<FaqCategory, Long>

View File

@ -0,0 +1,41 @@
package kr.co.vividnext.sodalive.faq
import kr.co.vividnext.sodalive.common.ApiResponse
import org.springframework.security.access.prepost.PreAuthorize
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.RestController
@RestController
@RequestMapping("/faq")
class FaqController(private val service: FaqService) {
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
fun createFaq(@RequestBody request: CreateFaqRequest) = ApiResponse.ok(
service.save(request),
"등록되었습니다."
)
@PutMapping
@PreAuthorize("hasRole('ADMIN')")
fun modifyFaq(@RequestBody request: ModifyFaqRequest) = ApiResponse.ok(
service.modify(request),
"수정되었습니다."
)
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
fun deleteCoin(@PathVariable id: Long) = ApiResponse.ok(service.delete(id), "삭제되었습니다.")
@GetMapping
fun getFaqList(@RequestParam("category") category: String) = ApiResponse.ok(service.getFaqList(category))
@GetMapping("/category")
fun getFaqCategoryList() = ApiResponse.ok(service.getCategoryList())
}

View File

@ -0,0 +1,64 @@
package kr.co.vividnext.sodalive.faq
import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.faq.QFaq.faq
import kr.co.vividnext.sodalive.faq.QFaqCategory.faqCategory
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface FaqRepository : JpaRepository<Faq, Long>
@Repository
class FaqQueryRepository(private val queryFactory: JPAQueryFactory) {
fun getCategory(category: String): FaqCategory? {
return queryFactory
.selectFrom(faqCategory)
.where(
faqCategory.isActive.isTrue
.and(faqCategory.category.eq(category))
)
.fetchFirst()
}
fun getCategoryList(): List<String> {
return queryFactory
.select(faqCategory.category)
.from(faqCategory)
.where(faqCategory.isActive.isTrue)
.orderBy(faqCategory.orders.desc())
.fetch()
}
fun getFaqList(category: String): List<GetFaqResponseItem> {
return queryFactory
.select(
QGetFaqResponseItem(
faq.id,
Expressions.asString(category),
faq.question,
faq.answer
)
)
.from(faq)
.innerJoin(faq.category, faqCategory)
.where(
faq.isActive.isTrue
.and(faqCategory.isActive.isTrue)
.and(faqCategory.category.eq(category))
)
.orderBy(faq.orders.desc())
.fetch()
}
fun getFaq(id: Long): Faq? {
return queryFactory
.selectFrom(faq)
.where(
faq.isActive.isTrue
.and(faq.id.eq(id))
)
.fetchFirst()
}
}

View File

@ -0,0 +1,62 @@
package kr.co.vividnext.sodalive.faq
import kr.co.vividnext.sodalive.common.SodaException
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
class FaqService(
private val repository: FaqRepository,
private val queryRepository: FaqQueryRepository
) {
@Transactional
fun save(request: CreateFaqRequest): Long {
if (request.question.isBlank()) throw SodaException("질문을 입력하세요.")
if (request.answer.isBlank()) throw SodaException("답변을 입력하세요.")
if (request.category.isBlank()) throw SodaException("카테고리를 선택하세요.")
val category = queryRepository.getCategory(request.category)
?: throw SodaException("잘못된 카테고리 입니다.")
val faq = Faq(request.question, request.answer)
faq.category = category
return repository.save(faq).id!!
}
@Transactional
fun modify(request: ModifyFaqRequest) {
val faq = queryRepository.getFaq(request.id)
?: throw SodaException("잘못된 요청입니다.")
if (request.question != null) {
if (request.question.isBlank()) throw SodaException("질문을 입력하세요.")
faq.question = request.question
}
if (request.answer != null) {
if (request.answer.isBlank()) throw SodaException("답변을 입력하세요.")
faq.answer = request.answer
}
if (request.category != null) {
if (request.category.isBlank()) throw SodaException("카테고리를 선택하세요.")
val category = queryRepository.getCategory(request.category) ?: throw SodaException("잘못된 카테고리 입니다.")
faq.category = category
}
}
@Transactional
fun delete(id: Long) {
if (id <= 0) throw SodaException("잘못된 요청입니다.")
val faq = repository.findByIdOrNull(id)
?: throw SodaException("잘못된 요청입니다.")
faq.isActive = false
}
fun getFaqList(category: String) = queryRepository.getFaqList(category)
fun getCategoryList() = queryRepository.getCategoryList()
}

View File

@ -0,0 +1,10 @@
package kr.co.vividnext.sodalive.faq
import com.querydsl.core.annotations.QueryProjection
data class GetFaqResponseItem @QueryProjection constructor(
val id: Long,
val category: String,
val question: String,
val answer: String
)

View File

@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.faq
data class ModifyFaqRequest(
val id: Long,
val question: String? = null,
val answer: String? = null,
val category: String? = null
)