From f2eeb6b9712dcaabb1c875f94087107ccba63c82 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:34:19 +0900 Subject: [PATCH 01/33] feat ( #11 ) : CreateFaqRequest --- .../in/faq/dto/request/CreateFaqRequest.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/CreateFaqRequest.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/CreateFaqRequest.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/CreateFaqRequest.kt new file mode 100644 index 0000000..c3d35d2 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/CreateFaqRequest.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request + +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Size + +data class CreateFaqRequest( + @field:NotBlank(message = "title은 null, 공백, 띄어쓰기를 허용하지 않습니다.") + @field:Size(max = 100, message = "title은 최대 100자까지 가능합니다.") + val title: String, + @field:NotBlank(message = "content은 null, 공백, 띄어쓰기를 허용하지 않습니다.") + @field:Size(max = 5000, message = "content는 최대 5000자까지 가능합니다.") + val content: String, + val faqType: FaqType, +) From 99aff373b8c583328205037ded96fab768a84f2f Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:34:28 +0900 Subject: [PATCH 02/33] feat ( #11 ) : UpdateFaqRequest --- .../in/faq/dto/request/UpdateFaqRequest.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/UpdateFaqRequest.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/UpdateFaqRequest.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/UpdateFaqRequest.kt new file mode 100644 index 0000000..5b352e7 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/request/UpdateFaqRequest.kt @@ -0,0 +1,15 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request + +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Size + +data class UpdateFaqRequest( + @field:NotBlank(message = "title은 null, 공백, 띄어쓰기를 허용하지 않습니다.") + @field:Size(max = 100, message = "title은 최대 100자까지 가능합니다.") + val title: String, + @field:NotBlank(message = "content은 null, 공백, 띄어쓰기를 허용하지 않습니다.") + @field:Size(max = 5000, message = "content는 최대 5000자까지 가능합니다.") + val content: String, + val faqType: FaqType, +) From 78d29af3715c2c54eb8ceeabb0db4e66dfca0e53 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:34:41 +0900 Subject: [PATCH 03/33] feat ( #11 ) : FaqDetailsResponse --- .../in/faq/dto/response/FaqDetailsResponse.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDetailsResponse.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDetailsResponse.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDetailsResponse.kt new file mode 100644 index 0000000..d367923 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDetailsResponse.kt @@ -0,0 +1,20 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response + +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import java.time.LocalDateTime + +/** + * FAQ 상세 조회 응답을 위한 데이터 클래스입니다. + * FAQ의 상세 내용을 보여줄 때 사용됩니다. + * + * @property title FAQ 제목 + * @property content FAQ 상세 내용 + * @property createdAt FAQ 생성 일시 + * @property faqType FAQ 유형 (카테고리) + */ +data class FaqDetailsResponse( + val title: String, + val content: String, + val createdAt: LocalDateTime, + val faqType: FaqType, +) From c89daede57490783b9ddb16d17551904cbaada55 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:34:50 +0900 Subject: [PATCH 04/33] feat ( #11 ) : FaqDto --- .../adapter/in/faq/dto/response/FaqDto.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDto.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDto.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDto.kt new file mode 100644 index 0000000..19363e4 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqDto.kt @@ -0,0 +1,23 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response + +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import java.time.LocalDateTime +import java.util.UUID + +/** + * FAQ 정보를 나타내는 데이터 전송 객체(DTO)입니다. + * FAQ 목록 조회 시 각 FAQ 항목의 정보를 담는 데 사용됩니다. + * + * @property id FAQ 고유 식별자 + * @property title FAQ 제목 + * @property content FAQ 내용 + * @property createdAt FAQ 생성 일시 + * @property faqType FAQ 유형 (카테고리) + */ +data class FaqDto( + val id: UUID, + val title: String, + val content: String, + val createdAt: LocalDateTime, + val faqType: FaqType, +) From 62c148036806cd3c78e0686673faf61f3214a047 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:35:00 +0900 Subject: [PATCH 05/33] feat ( #11 ) : FaqListResponse --- .../adapter/in/faq/dto/response/FaqListResponse.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqListResponse.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqListResponse.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqListResponse.kt new file mode 100644 index 0000000..48512de --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqListResponse.kt @@ -0,0 +1,11 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response + +/** + * FAQ 목록 응답을 위한 데이터 클래스입니다. + * 여러 개의 FAQ 정보를 리스트 형태로 반환할 때 사용됩니다. + * + * @property faqs FAQ 정보 목록 (FaqDto 리스트) + */ +data class FaqListResponse( + val faqs: List, +) From 99415441bde1c5cb3a530bd9b271d10de0492e51 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:35:14 +0900 Subject: [PATCH 06/33] feat ( #11 ) : FaqTitleAndTypeResponse --- .../dto/response/FaqTitleAndTypeResponse.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleAndTypeResponse.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleAndTypeResponse.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleAndTypeResponse.kt new file mode 100644 index 0000000..37537d7 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleAndTypeResponse.kt @@ -0,0 +1,18 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response + +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import java.util.UUID + +/** + * FAQ 제목과 유형 정보를 포함하는 응답 데이터 클래스입니다. + * FAQ 목록에서 제목과 유형을 함께 보여줄 때 사용됩니다. + * + * @property id FAQ 고유 식별자 + * @property type FAQ 유형 (카테고리) + * @property title FAQ 제목 + */ +data class FaqTitleAndTypeResponse( + val id: UUID, + val type: FaqType, + val title: String, +) From 0c58e28d5cf0b785a6047cfa70f167bc790bb2e8 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:35:20 +0900 Subject: [PATCH 07/33] feat ( #11 ) : FaqTitleResponse --- .../in/faq/dto/response/FaqTitleResponse.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleResponse.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleResponse.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleResponse.kt new file mode 100644 index 0000000..5b9289c --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/dto/response/FaqTitleResponse.kt @@ -0,0 +1,17 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response + +import java.util.UUID + +/** + * FAQ 제목 목록 조회 응답을 위한 데이터 클래스입니다. + * FAQ 목록에서 제목과 간략한 내용을 보여줄 때 사용됩니다. + * + * @property id FAQ 고유 식별자 + * @property title FAQ 제목 + * @property content FAQ 내용 (요약된 내용일 수 있음) + */ +data class FaqTitleResponse( + val id: UUID, + val title: String, + val content: String, +) From 2a36d689a1d6e0d68bd256376a1f49c3fe3fbf23 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:35:26 +0900 Subject: [PATCH 08/33] feat ( #11 ) : FaqWebAdapter --- .../feed/adapter/in/faq/FaqWebAdapter.kt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/FaqWebAdapter.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/FaqWebAdapter.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/FaqWebAdapter.kt new file mode 100644 index 0000000..2ccf174 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/in/faq/FaqWebAdapter.kt @@ -0,0 +1,115 @@ +package hs.kr.entrydsm.feed.adapter.`in`.faq + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request.CreateFaqRequest +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request.UpdateFaqRequest +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqDetailsResponse +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqListResponse +import hs.kr.entrydsm.feed.application.faq.port.`in`.CreateFaqUseCase +import hs.kr.entrydsm.feed.application.faq.port.`in`.DeleteFaqUseCase +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryFaqDetailsUseCase +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryFaqListByTypeUseCase +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryFaqListUseCase +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryTopFaqUseCase +import hs.kr.entrydsm.feed.application.faq.port.`in`.UpdateFaqUseCase +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import org.springframework.http.HttpStatus +import org.springframework.validation.annotation.Validated +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +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.ResponseStatus +import org.springframework.web.bind.annotation.RestController +import java.util.UUID + +/** + * FAQ 관련 HTTP 요청을 처리하는 웹 어댑터 클래스입니다. + * + * 이 클래스는 FAQ와 관련된 모든 HTTP 엔드포인트를 제공하며, + * 클라이언트의 요청을 적절한 서비스 메서드로 라우팅합니다. + * + * @property faqService FAQ 비즈니스 로직을 처리하는 서비스 + */ +@RequestMapping("/faq") +@RestController +class FaqWebAdapter( + private val createFaqUseCase: CreateFaqUseCase, + private val deleteFaqUseCase: DeleteFaqUseCase, + private val queryFaqDetailsUseCase: QueryFaqDetailsUseCase, + private val queryFaqListByTypeUseCase: QueryFaqListByTypeUseCase, + private val queryFaqListUseCase: QueryFaqListUseCase, + private val queryTopFaqUseCase: QueryTopFaqUseCase, + private val updateFaqUseCase: UpdateFaqUseCase, +) { + @ResponseStatus(HttpStatus.CREATED) + @PostMapping + fun createFaq( + @RequestBody @Validated + createFaqRequest: CreateFaqRequest, + ) = createFaqUseCase.execute(createFaqRequest) + + /** + * 특정 FAQ의 상세 정보를 조회합니다. + * + * @param faqId 조회할 FAQ의 고유 식별자 + * @return FAQ 상세 정보가 포함된 응답 객체 + */ + @GetMapping("/{faq-id}") + fun queryFaqDetails( + @PathVariable("faq-id") faqId: UUID, + ): FaqDetailsResponse = queryFaqDetailsUseCase.execute(faqId) + + /** + * 특정 유형의 FAQ 목록을 조회합니다. + * + * @param faqType 조회할 FAQ 유형 + * @return 해당 유형의 FAQ 목록이 포함된 응답 객체 + */ + @GetMapping + fun queryFaqListByType( + @RequestParam("type") faqType: FaqType, + ): FaqListResponse = queryFaqListByTypeUseCase.execute(faqType) + + /** + * 모든 FAQ 목록을 조회합니다. + * + * @return 모든 FAQ 목록이 포함된 응답 객체 + */ + @GetMapping("/all") + fun queryFaqList(): FaqListResponse = queryFaqListUseCase.execute() + + /** + * 최근에 등록된 FAQ 목록을 조회합니다. + * + * @return 최근 FAQ 목록이 포함된 응답 객체 + */ + @GetMapping("/recently") + fun queryTopFaq() = queryTopFaqUseCase.execute() + + /** + * 기존 FAQ를 수정합니다. + * + * @param faqId 수정할 FAQ의 고유 식별자 + * @param updateFaqRequest FAQ 수정 요청 데이터 + */ + @PatchMapping("/{faq-id}") + fun updateFaq( + @PathVariable("faq-id") faqId: UUID, + @RequestBody @Validated + updateFaqRequest: UpdateFaqRequest, + ) = updateFaqUseCase.execute(faqId, updateFaqRequest) + + /** + * 특정 FAQ를 삭제합니다. + * + * @param faqId 삭제할 FAQ의 고유 식별자 + */ + @DeleteMapping("/{faq-id}") + fun deleteFaq( + @PathVariable("faq-id") faqId: UUID, + ) = deleteFaqUseCase.execute(faqId) +} From d4d0246d825c00ed6365a49429805a5f4dda5b21 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:35:36 +0900 Subject: [PATCH 09/33] feat ( #11 ) : FaqJpaEntity --- .../adapter/out/entity/faq/FaqJpaEntity.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/entity/faq/FaqJpaEntity.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/entity/faq/FaqJpaEntity.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/entity/faq/FaqJpaEntity.kt new file mode 100644 index 0000000..c3d8457 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/entity/faq/FaqJpaEntity.kt @@ -0,0 +1,31 @@ +package hs.kr.entrydsm.feed.adapter.out.entity.faq + +import hs.kr.entrydsm.feed.global.entity.BaseEntity +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import java.util.UUID + +/** + * FAQ 정보를 데이터베이스에 저장하기 위한 JPA 엔티티 클래스입니다. + * + * @property title FAQ 제목 (최대 100자) + * @property content FAQ 내용 (최대 5000자) + * @property faqType FAQ 유형 (FaqType ENUM) + * @property adminId FAQ를 작성한 관리자 ID (UUID) + * @param id 엔티티의 고유 식별자 (생성 시 자동 생성됨) + */ +@Entity(name = "tbl_faq") +class FaqJpaEntity( + id: UUID? = null, + @Column(name = "title", length = 100, nullable = false) + var title: String, + @Column(name = "content", length = 5000, nullable = false) + var content: String, + @Enumerated(EnumType.STRING) + var faqType: FaqType, + @Column(name = "admin_id", columnDefinition = "BINARY(16)", nullable = false) + var adminId: UUID, +) : BaseEntity(id) From b67b3ea8c5b1825d7ef70a56fa55afddf76aff58 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:35:49 +0900 Subject: [PATCH 10/33] feat ( #11 ) : FaqMapper --- .../feed/adapter/out/mapper/faq/FaqMapper.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/mapper/faq/FaqMapper.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/mapper/faq/FaqMapper.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/mapper/faq/FaqMapper.kt new file mode 100644 index 0000000..cbfb69b --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/mapper/faq/FaqMapper.kt @@ -0,0 +1,28 @@ +package hs.kr.entrydsm.feed.adapter.out.mapper.faq + +import hs.kr.entrydsm.feed.adapter.out.entity.faq.FaqJpaEntity +import hs.kr.entrydsm.feed.model.faq.Faq +import org.mapstruct.Mapper + +/** + * FAQ 도메인 모델과 JPA 엔티티 간의 변환을 담당하는 매퍼 인터페이스입니다. + * MapStruct를 사용하여 구현체가 자동으로 생성됩니다. + */ +@Mapper(componentModel = "spring") +interface FaqMapper { + /** + * JPA 엔티티를 FAQ 도메인 모델로 변환합니다. + * + * @param entity 변환할 FaqJpaEntity 인스턴스 + * @return 변환된 FAQ 도메인 모델 + */ + fun toModel(entity: FaqJpaEntity): Faq + + /** + * FAQ 도메인 모델을 JPA 엔티티로 변환합니다. + * + * @param model 변환할 FAQ 도메인 모델 + * @return 변환된 FaqJpaEntity 인스턴스 + */ + fun toEntity(model: Faq): FaqJpaEntity +} From 5c6d9a9fa719ae34914c6d1278b89baee149efa4 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:37:58 +0900 Subject: [PATCH 11/33] feat ( #11 ) : FaqPersistenceAdapter --- .../persistence/faq/FaqPersistenceAdapter.kt | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/FaqPersistenceAdapter.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/FaqPersistenceAdapter.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/FaqPersistenceAdapter.kt new file mode 100644 index 0000000..9dbf9ca --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/FaqPersistenceAdapter.kt @@ -0,0 +1,80 @@ +package hs.kr.entrydsm.feed.adapter.out.persistence.faq + +import hs.kr.entrydsm.feed.adapter.out.mapper.faq.FaqMapper +import hs.kr.entrydsm.feed.adapter.out.persistence.faq.repository.FaqRepository +import hs.kr.entrydsm.feed.application.faq.port.out.DeleteFaqPort +import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort +import hs.kr.entrydsm.feed.application.faq.port.out.SaveFaqPort +import hs.kr.entrydsm.feed.model.faq.Faq +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Component +import java.util.UUID + +/** + * FAQ 도메인과 데이터베이스 간의 상호작용을 담당하는 어댑터 클래스입니다. + * + * @property faqRepository FAQ 엔티티를 데이터베이스에서 조작하기 위한 리포지토리 + * @property faqMapper FAQ 도메인 객체와 엔티티 간의 변환을 담당하는 매퍼 + */ +@Component +class FaqPersistenceAdapter( + private val faqRepository: FaqRepository, + private val faqMapper: FaqMapper, +) : SaveFaqPort, DeleteFaqPort, FindFaqPort { + + /** + * FAQ를 저장하거나 업데이트합니다. + * + * @param faq 저장할 FAQ 도메인 모델 + * @return 저장된 FAQ 도메인 모델 + */ + override fun saveFaq(faq: Faq): Faq = faqMapper.toModel(faqRepository.save(faqMapper.toEntity(faq))) + + /** + * FAQ를 삭제합니다. + * + * @param faq 삭제할 FAQ 도메인 모델 + */ + override fun deleteFaq(faq: Faq) { + faqRepository.delete(faqMapper.toEntity(faq)) + } + + /** + * ID로 FAQ를 조회합니다. + * + * @param faqId 조회할 FAQ의 고유 식별자 + * @return 조회된 FAQ 도메인 모델, 존재하지 않을 경우 null 반환 + */ + override fun findByIdOrNull(faqId: UUID): Faq? = + faqRepository.findByIdOrNull(faqId)?.let { faqMapper.toModel(it) } + + /** + * 특정 유형의 FAQ 목록을 조회합니다. + * + * @param faqType 조회할 FAQ 유형 + * @return 조회된 FAQ 도메인 모델 목록 + */ + override fun findAllByFaqType(faqType: FaqType): List { + return faqRepository.findAllByFaqType(faqType).map { faqMapper.toModel(it) } + } + + /** + * 모든 FAQ 목록을 조회합니다. + * + * @return 모든 FAQ 도메인 모델 목록 + */ + override fun findAll(): List { + return faqRepository.findAll().map { faqMapper.toModel(it) } + } + + /** + * 최근에 생성된 상위 5개의 FAQ를 조회합니다. + * 생성일자 기준 내림차순으로 정렬됩니다. + * + * @return 최근 FAQ 5개의 도메인 모델 목록 + */ + override fun findTop5ByOrderByCreatedAtDesc(): List { + return faqRepository.findTop5ByOrderByCreatedAtDesc().map { faqMapper.toModel(it) } + } +} From bcfa7ee45c9fa9614a5e940d9b8afbfbee1805ba Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:38:14 +0900 Subject: [PATCH 12/33] feat ( #11 ) : FaqNotFoundException --- .../faq/exception/FaqNotFoundException.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/exception/FaqNotFoundException.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/exception/FaqNotFoundException.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/exception/FaqNotFoundException.kt new file mode 100644 index 0000000..1e2aaec --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/exception/FaqNotFoundException.kt @@ -0,0 +1,14 @@ +package hs.kr.entrydsm.feed.application.faq.exception + +import hs.kr.entrydsm.feed.global.error.exception.CasperException +import hs.kr.entrydsm.feed.global.error.exception.ErrorCode + +/** + * FAQ을 찾을 수 없을 때 발생하는 예외 클래스입니다. + * + * @property status HTTP 상태 코드 (404) + * @property message 에러 메시지 + */ +object FaqNotFoundException : CasperException( + ErrorCode.FAQ_NOT_FOUND, +) From 8c9f8b7976b52cdf1dc747ef896393fda17dd057 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:38:23 +0900 Subject: [PATCH 13/33] feat ( #11 ) : CreateFaqUseCase --- .../application/faq/port/in/CreateFaqUseCase.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/CreateFaqUseCase.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/CreateFaqUseCase.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/CreateFaqUseCase.kt new file mode 100644 index 0000000..cc173bb --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/CreateFaqUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.feed.application.faq.port.`in` + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request.CreateFaqRequest + +/** + * FAQ 생성을 위한 유스케이스 인터페이스입니다. + * FAQ 도메인에서 새로운 FAQ를 생성하는 역할을 담당합니다. + */ +interface CreateFaqUseCase { + /** + * 새로운 FAQ를 생성합니다. + * + * @param createFaqRequest FAQ 생성 요청 데이터 + */ + fun execute(createFaqRequest: CreateFaqRequest) +} From 011814850e94277eac2f8f34f5e9f0f72c30f47e Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:38:32 +0900 Subject: [PATCH 14/33] feat ( #11 ) : DeleteFaqUseCase --- .../application/faq/port/in/DeleteFaqUseCase.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/DeleteFaqUseCase.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/DeleteFaqUseCase.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/DeleteFaqUseCase.kt new file mode 100644 index 0000000..7e5ae7b --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/DeleteFaqUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.feed.application.faq.port.`in` + +import java.util.UUID + +/** + * FAQ 삭제를 위한 유스케이스 인터페이스입니다. + * FAQ 도메인에서 특정 FAQ를 삭제하는 역할을 담당합니다. + */ +interface DeleteFaqUseCase { + /** + * 특정 FAQ를 삭제합니다. + * + * @param faqId 삭제할 FAQ의 고유 식별자 + */ + fun execute(faqId: UUID) +} From 92334a5d6dc41ee31582e8efeacf5c2b59cf949a Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:38:46 +0900 Subject: [PATCH 15/33] feat ( #11 ) : QueryFaqDetailsUseCase --- .../faq/port/in/QueryFaqDetailsUseCase.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqDetailsUseCase.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqDetailsUseCase.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqDetailsUseCase.kt new file mode 100644 index 0000000..f5b702a --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqDetailsUseCase.kt @@ -0,0 +1,18 @@ +package hs.kr.entrydsm.feed.application.faq.port.`in` + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqDetailsResponse +import java.util.UUID + +/** + * FAQ 상세 정보 조회를 위한 유스케이스 인터페이스입니다. + * FAQ 도메인에서 특정 FAQ의 상세 정보를 조회하는 역할을 담당합니다. + */ +interface QueryFaqDetailsUseCase { + /** + * 특정 FAQ의 상세 정보를 조회합니다. + * + * @param faqId 조회할 FAQ의 고유 식별자 + * @return FAQ 상세 정보 응답 + */ + fun execute(faqId: UUID): FaqDetailsResponse +} From 7eeb82ef8149e39c46d6b62d16e776097ef59808 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:38:55 +0900 Subject: [PATCH 16/33] feat ( #11 ) : QueryFaqListByTypeUseCase --- .../faq/port/in/QueryFaqListByTypeUseCase.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListByTypeUseCase.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListByTypeUseCase.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListByTypeUseCase.kt new file mode 100644 index 0000000..f92c9bb --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListByTypeUseCase.kt @@ -0,0 +1,18 @@ +package hs.kr.entrydsm.feed.application.faq.port.`in` + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqListResponse +import hs.kr.entrydsm.feed.model.faq.type.FaqType + +/** + * 특정 유형의 FAQ 목록 조회를 위한 유스케이스 인터페이스입니다. + * FAQ 도메인에서 특정 유형에 해당하는 FAQ 목록을 조회하는 역할을 담당합니다. + */ +interface QueryFaqListByTypeUseCase { + /** + * 특정 유형의 FAQ 목록을 조회합니다. + * + * @param faqType 조회할 FAQ 유형 + * @return FAQ 목록 응답 + */ + fun execute(faqType: FaqType): FaqListResponse +} From b175771f34941a077b2486134e1e78b5ab8ef05f Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:39:05 +0900 Subject: [PATCH 17/33] feat ( #11 ) : QueryFaqListUseCase --- .../faq/port/in/QueryFaqListUseCase.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListUseCase.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListUseCase.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListUseCase.kt new file mode 100644 index 0000000..fb89eab --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryFaqListUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.feed.application.faq.port.`in` + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqListResponse + +/** + * FAQ 목록 조회를 위한 유스케이스 인터페이스입니다. + * FAQ 도메인에서 모든 FAQ 목록을 조회하는 역할을 담당합니다. + */ +interface QueryFaqListUseCase { + /** + * 모든 FAQ 목록을 조회합니다. + * + * @return FAQ 목록 응답 + */ + fun execute(): FaqListResponse +} From fa7216ffeaf13e057ddea541fe433ec1e8c012c3 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:39:16 +0900 Subject: [PATCH 18/33] feat ( #11 ) : QueryTopFaqUseCase --- .../faq/port/in/QueryTopFaqUseCase.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryTopFaqUseCase.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryTopFaqUseCase.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryTopFaqUseCase.kt new file mode 100644 index 0000000..1553574 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/QueryTopFaqUseCase.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.feed.application.faq.port.`in` + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqTitleResponse + +/** + * 상위 FAQ 조회를 위한 유스케이스 인터페이스입니다. + * FAQ 도메인에서 상위에 노출될 FAQ 목록을 조회하는 역할을 담당합니다. + */ +interface QueryTopFaqUseCase { + /** + * 상위 FAQ 목록을 조회합니다. + * + * @return FAQ 제목 목록 응답 + */ + fun execute(): List +} From fe6210dc05c95a40af8907920eded34918d9b3f1 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:39:30 +0900 Subject: [PATCH 19/33] feat ( #11 ) : UpdateFaqUseCase --- .../faq/port/in/UpdateFaqUseCase.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/UpdateFaqUseCase.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/UpdateFaqUseCase.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/UpdateFaqUseCase.kt new file mode 100644 index 0000000..2953f43 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/in/UpdateFaqUseCase.kt @@ -0,0 +1,21 @@ +package hs.kr.entrydsm.feed.application.faq.port.`in` + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request.UpdateFaqRequest +import java.util.UUID + +/** + * FAQ 수정을 위한 유스케이스 인터페이스입니다. + * FAQ 도메인에서 기존 FAQ를 수정하는 역할을 담당합니다. + */ +interface UpdateFaqUseCase { + /** + * 특정 FAQ를 수정합니다. + * + * @param faqId 수정할 FAQ의 고유 식별자 + * @param updateFaqRequest FAQ 수정 요청 데이터 + */ + fun execute( + faqId: UUID, + updateFaqRequest: UpdateFaqRequest, + ) +} From 6e14f929fc1b2941d6abd1856f379e00dd755b89 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:39:43 +0900 Subject: [PATCH 20/33] feat ( #11 ) : DeleteFaqPort --- .../application/faq/port/out/DeleteFaqPort.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/DeleteFaqPort.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/DeleteFaqPort.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/DeleteFaqPort.kt new file mode 100644 index 0000000..b743fc5 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/DeleteFaqPort.kt @@ -0,0 +1,16 @@ +package hs.kr.entrydsm.feed.application.faq.port.out + +import hs.kr.entrydsm.feed.model.faq.Faq + +/** + * FAQ 삭제를 위한 포트 인터페이스입니다. + * FAQ 도메인 객체를 삭제하는 메서드를 정의합니다. + */ +interface DeleteFaqPort { + /** + * FAQ를 삭제합니다. + * + * @param faq 삭제할 FAQ 도메인 객체 + */ + fun deleteFaq(faq: Faq) +} From e5cf9ebe107784e97598e969665d833ffee6a068 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:39:49 +0900 Subject: [PATCH 21/33] feat ( #11 ) : FindFaqPort --- .../application/faq/port/out/FindFaqPort.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/FindFaqPort.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/FindFaqPort.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/FindFaqPort.kt new file mode 100644 index 0000000..1924607 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/FindFaqPort.kt @@ -0,0 +1,41 @@ +package hs.kr.entrydsm.feed.application.faq.port.out + +import hs.kr.entrydsm.feed.model.faq.Faq +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import java.util.UUID + +/** + * FAQ 조회를 위한 포트 인터페이스입니다. + * FAQ 도메인 객체를 다양한 방식으로 조회하는 메서드를 정의합니다. + */ +interface FindFaqPort { + /** + * FAQ ID로 FAQ를 조회합니다. + * + * @param faqId 조회할 FAQ의 고유 식별자 + * @return 조회된 FAQ 객체, 없을 경우 null + */ + fun findByIdOrNull(faqId: UUID): Faq? + + /** + * 특정 유형의 모든 FAQ를 조회합니다. + * + * @param faqType 조회할 FAQ 유형 + * @return 해당 유형의 FAQ 목록 (없을 경우 빈 목록) + */ + fun findAllByFaqType(faqType: FaqType): List + + /** + * 모든 FAQ를 조회합니다. + * + * @return 모든 FAQ 목록 (없을 경우 빈 목록) + */ + fun findAll(): List + + /** + * 생성일시 기준으로 최신 FAQ 5개를 조회합니다. + * + * @return 최신순으로 정렬된 FAQ 5개 목록 (5개 미만일 경우 모두 반환) + */ + fun findTop5ByOrderByCreatedAtDesc(): List +} From 048cc83c8da4c25396c8d8fea36c1784f447503e Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:40:02 +0900 Subject: [PATCH 22/33] feat ( #11 ) : SaveFaqPort --- .../application/faq/port/out/SaveFaqPort.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/SaveFaqPort.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/SaveFaqPort.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/SaveFaqPort.kt new file mode 100644 index 0000000..7a9583e --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/port/out/SaveFaqPort.kt @@ -0,0 +1,17 @@ +package hs.kr.entrydsm.feed.application.faq.port.out + +import hs.kr.entrydsm.feed.model.faq.Faq + +/** + * FAQ 저장을 위한 포트 인터페이스입니다. + * FAQ 도메인 객체를 저장하는 메서드를 정의합니다. + */ +interface SaveFaqPort { + /** + * FAQ를 저장하거나 업데이트합니다. + * + * @param faq 저장할 FAQ 도메인 객체 + * @return 저장된 FAQ 도메인 객체 (ID가 할당됨) + */ + fun saveFaq(faq: Faq): Faq +} From a1f03bb5482f5814e3e67c9dd0dcc6d581819b18 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:40:13 +0900 Subject: [PATCH 23/33] feat ( #11 ) : CreateFaqService --- .../faq/service/CreateFaqService.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/CreateFaqService.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/CreateFaqService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/CreateFaqService.kt new file mode 100644 index 0000000..992d906 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/CreateFaqService.kt @@ -0,0 +1,38 @@ +package hs.kr.entrydsm.feed.application.faq.service + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request.CreateFaqRequest +import hs.kr.entrydsm.feed.application.faq.port.`in`.CreateFaqUseCase +import hs.kr.entrydsm.feed.application.faq.port.out.SaveFaqPort +import hs.kr.entrydsm.feed.global.utils.user.UserUtils +import hs.kr.entrydsm.feed.model.faq.Faq +import org.springframework.stereotype.Service + +/** + * FAQ 생성을 처리하는 서비스 클래스입니다. + * + * @property saveFaqPort FAQ 저장을 위한 포트 + * @property userUtils 현재 사용자 정보를 조회하기 위한 유틸리티 + */ +@Service +class CreateFaqService( + private val saveFaqPort: SaveFaqPort, + private val userUtils: UserUtils, +) : CreateFaqUseCase { + /** + * 새로운 FAQ를 생성합니다. + * + * @param createFaqRequest FAQ 생성 요청 데이터 + * @throws hs.kr.entrydsm.feed.global.exception.UnauthorizedException 현재 사용자가 인증되지 않은 경우 + */ + override fun execute(createFaqRequest: CreateFaqRequest) { + val faq = + Faq( + title = createFaqRequest.title, + content = createFaqRequest.content, + faqType = createFaqRequest.faqType, + adminId = userUtils.getCurrentUserId(), + ) + + saveFaqPort.saveFaq(faq) + } +} From 69b6a97aea64aea32bcd0a3dfa69eae0ea2964ed Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:40:25 +0900 Subject: [PATCH 24/33] feat ( #11 ) : DeleteFaqService --- .../faq/service/DeleteFaqService.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/DeleteFaqService.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/DeleteFaqService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/DeleteFaqService.kt new file mode 100644 index 0000000..5b28745 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/DeleteFaqService.kt @@ -0,0 +1,31 @@ +package hs.kr.entrydsm.feed.application.faq.service + +import hs.kr.entrydsm.feed.application.faq.exception.FaqNotFoundException +import hs.kr.entrydsm.feed.application.faq.port.`in`.DeleteFaqUseCase +import hs.kr.entrydsm.feed.application.faq.port.out.DeleteFaqPort +import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort +import org.springframework.stereotype.Service +import java.util.UUID + +/** + * FAQ 삭제를 처리하는 서비스 클래스입니다. + * + * @property findFaqPort FAQ 조회를 위한 포트 + * @property deleteFaqPort FAQ 삭제를 위한 포트 + */ +@Service +class DeleteFaqService( + private val findFaqPort: FindFaqPort, + private val deleteFaqPort: DeleteFaqPort, +) : DeleteFaqUseCase { + /** + * 지정된 ID의 FAQ를 삭제합니다. + * + * @param faqId 삭제할 FAQ의 고유 식별자 + * @throws hs.kr.entrydsm.feed.application.faq.exception.FaqNotFoundException 지정된 ID의 FAQ를 찾을 수 없는 경우 + */ + override fun execute(faqId: UUID) { + val faq = findFaqPort.findByIdOrNull(faqId) ?: throw FaqNotFoundException + deleteFaqPort.deleteFaq(faq) + } +} From c391de43488263170b2703ae6e8b2f475e4fd370 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:40:36 +0900 Subject: [PATCH 25/33] feat ( #11 ) : QueryFaqDetailsService --- .../faq/service/QueryFaqDetailsService.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqDetailsService.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqDetailsService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqDetailsService.kt new file mode 100644 index 0000000..8d0b89c --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqDetailsService.kt @@ -0,0 +1,35 @@ +package hs.kr.entrydsm.feed.application.faq.service + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqDetailsResponse +import hs.kr.entrydsm.feed.application.faq.exception.FaqNotFoundException +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryFaqDetailsUseCase +import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort +import org.springframework.stereotype.Service +import java.util.UUID + +/** + * FAQ 상세 조회를 처리하는 서비스 클래스입니다. + * + * @property findFaqPort FAQ 조회를 위한 포트 + */ +@Service +class QueryFaqDetailsService( + private val findFaqPort: FindFaqPort, +) : QueryFaqDetailsUseCase { + /** + * 지정된 ID의 FAQ 상세 정보를 조회합니다. + * + * @param faqId 조회할 FAQ의 고유 식별자 + * @return FAQ 상세 정보 응답 + * @throws hs.kr.entrydsm.feed.application.faq.exception.FaqNotFoundException 지정된 ID의 FAQ를 찾을 수 없는 경우 + */ + override fun execute(faqId: UUID): FaqDetailsResponse { + val faq = findFaqPort.findByIdOrNull(faqId) ?: throw FaqNotFoundException + return FaqDetailsResponse( + title = faq.title, + content = faq.content, + createdAt = faq.createdAt, + faqType = faq.faqType, + ) + } +} From 8a16117b22f398f5b4b829cbfd59b8916135cf69 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:40:51 +0900 Subject: [PATCH 26/33] feat ( #11 ) : Que --- .../faq/service/QueryFaqListByTypeService.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListByTypeService.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListByTypeService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListByTypeService.kt new file mode 100644 index 0000000..9aa1697 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListByTypeService.kt @@ -0,0 +1,38 @@ +package hs.kr.entrydsm.feed.application.faq.service + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqDto +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqListResponse +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryFaqListByTypeUseCase +import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import org.springframework.stereotype.Service + +/** + * 특정 유형의 FAQ 목록 조회를 처리하는 서비스 클래스입니다. + * + * @property findFaqPort FAQ 조회를 위한 포트 + */ +@Service +class QueryFaqListByTypeService( + private val findFaqPort: FindFaqPort, +) : QueryFaqListByTypeUseCase { + /** + * 지정된 유형의 FAQ 목록을 조회합니다. + * + * @param faqType 조회할 FAQ 유형 + * @return FAQ 목록 응답 + */ + override fun execute(faqType: FaqType): FaqListResponse { + val faqs = + findFaqPort.findAllByFaqType(faqType).map { + FaqDto( + id = it.id!!, + title = it.title, + content = it.content, + createdAt = it.createdAt, + faqType = it.faqType, + ) + } + return FaqListResponse(faqs) + } +} From 81d21a5c9317b0189ee2a036c1f6e0ce147948c4 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:41:04 +0900 Subject: [PATCH 27/33] feat ( #11 ) : QueryFaqListService --- .../faq/service/QueryFaqListService.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListService.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListService.kt new file mode 100644 index 0000000..9602d1c --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryFaqListService.kt @@ -0,0 +1,36 @@ +package hs.kr.entrydsm.feed.application.faq.service + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqDto +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqListResponse +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryFaqListUseCase +import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort +import org.springframework.stereotype.Service + +/** + * FAQ 목록 조회를 처리하는 서비스 클래스입니다. + * + * @property findFaqPort FAQ 조회를 위한 포트 + */ +@Service +class QueryFaqListService( + private val findFaqPort: FindFaqPort, +) : QueryFaqListUseCase { + /** + * 모든 FAQ 목록을 조회합니다. + * + * @return FAQ 목록 응답 + */ + override fun execute(): FaqListResponse { + val faqs = + findFaqPort.findAll().map { + FaqDto( + id = it.id!!, + title = it.title, + content = it.content, + createdAt = it.createdAt, + faqType = it.faqType, + ) + } + return FaqListResponse(faqs) + } +} From 10c8a8096f8b9fafd8f0688c0667570f0d9658fe Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:41:15 +0900 Subject: [PATCH 28/33] feat ( #11 ) : QueryTopFaqService --- .../faq/service/QueryTopFaqService.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryTopFaqService.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryTopFaqService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryTopFaqService.kt new file mode 100644 index 0000000..995244b --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/QueryTopFaqService.kt @@ -0,0 +1,32 @@ +package hs.kr.entrydsm.feed.application.faq.service + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.response.FaqTitleResponse +import hs.kr.entrydsm.feed.application.faq.port.`in`.QueryTopFaqUseCase +import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort +import org.springframework.stereotype.Service + +/** + * 상위 FAQ 조회를 처리하는 서비스 클래스입니다. + * 최근에 생성된 FAQ 중 상위 5개를 조회합니다. + * + * @property findFaqPort FAQ 조회를 위한 포트 + */ +@Service +class QueryTopFaqService( + private val findFaqPort: FindFaqPort, +) : QueryTopFaqUseCase { + /** + * 최근에 생성된 상위 5개의 FAQ 제목 목록을 조회합니다. + * + * @return FAQ 제목 목록 (최대 5개) + */ + override fun execute(): List { + return findFaqPort.findTop5ByOrderByCreatedAtDesc().map { + FaqTitleResponse( + it.id!!, + it.title, + it.content, + ) + } + } +} From 737a0b17a0fea4a8f82b7c40b60888316f35723c Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:41:30 +0900 Subject: [PATCH 29/33] feat ( #11 ) : UpdateFaqService --- .../faq/service/UpdateFaqService.kt | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt new file mode 100644 index 0000000..d5b239c --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt @@ -0,0 +1,44 @@ +package hs.kr.entrydsm.feed.application.faq.service + +import hs.kr.entrydsm.feed.adapter.`in`.faq.dto.request.UpdateFaqRequest +import hs.kr.entrydsm.feed.application.faq.exception.FaqNotFoundException +import hs.kr.entrydsm.feed.application.faq.port.`in`.UpdateFaqUseCase +import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort +import hs.kr.entrydsm.feed.global.utils.user.UserUtils +import org.springframework.stereotype.Service +import java.util.UUID + +/** + * FAQ 수정을 처리하는 서비스 클래스입니다. + * + * @property findFaqPort FAQ 조회를 위한 포트 + * @property userUtils 현재 사용자 정보를 조회하기 위한 유틸리티 + */ +@Service +class UpdateFaqService( + private val findFaqPort: FindFaqPort, + private val userUtils: UserUtils, +) : UpdateFaqUseCase { + /** + * 지정된 ID의 FAQ를 업데이트합니다. + * + * @param faqId 업데이트할 FAQ의 고유 식별자 + * @param updateFaqRequest FAQ 업데이트 요청 데이터 + * @throws hs.kr.entrydsm.feed.application.faq.exception.FaqNotFoundException 지정된 ID의 FAQ를 찾을 수 없는 경우 + * @throws hs.kr.entrydsm.feed.global.exception.UnauthorizedException 현재 사용자에게 권한이 없는 경우 + */ + override fun execute( + faqId: UUID, + updateFaqRequest: UpdateFaqRequest, + ) { + val faq = findFaqPort.findByIdOrNull(faqId) ?: throw FaqNotFoundException + updateFaqRequest.run { + faq.updateFaq( + newTitle = title, + newContent = content, + newFaqType = faqType, + newAdminId = userUtils.getCurrentUserId(), + ) + } + } +} From 6e2d636d51a61ee4ae39a269cf3d4b6488f28ae5 Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:41:41 +0900 Subject: [PATCH 30/33] feat ( #11 ) : FaqType --- .../kotlin/hs/kr/entrydsm/feed/model/faq/type/FaqType.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/type/FaqType.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/type/FaqType.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/type/FaqType.kt new file mode 100644 index 0000000..64869a1 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/type/FaqType.kt @@ -0,0 +1,9 @@ +package hs.kr.entrydsm.feed.model.faq.type + +enum class FaqType { + ADMISSION, + COURSE, + SCHOOL_LIFE, + DORMITORY, + OTHER, +} From f7e95d377b8093d6927aa3b5bdae8b0cd8979b3b Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:41:47 +0900 Subject: [PATCH 31/33] feat ( #11 ) : Faq --- .../hs/kr/entrydsm/feed/model/faq/Faq.kt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/Faq.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/Faq.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/Faq.kt new file mode 100644 index 0000000..52e8997 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/model/faq/Faq.kt @@ -0,0 +1,47 @@ +package hs.kr.entrydsm.feed.model.faq + +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import java.time.LocalDateTime +import java.util.UUID + +/** + * 자주 묻는 질문(FAQ) 도메인 모델 클래스입니다. + * + * @property id FAQ의 고유 식별자 (생성 시 자동 할당) + * @property title FAQ 제목 + * @property content FAQ 내용 + * @property faqType FAQ 유형 (enum) + * @property adminId FAQ를 작성한 관리자 ID + * @property createdAt FAQ 생성 일시 (기본값: 현재 시간) + */ +data class Faq( + val id: UUID? = null, + val title: String, + val content: String, + val faqType: FaqType, + val adminId: UUID, + val createdAt: LocalDateTime = LocalDateTime.now(), +) { + /** + * FAQ 정보를 업데이트합니다. + * + * @param newTitle 새로운 제목 + * @param newContent 새로운 내용 + * @param newFaqType 새로운 FAQ 유형 + * @param newAdminId 수정한 관리자 ID + * @return 업데이트된 FAQ 객체 + */ + fun updateFaq( + newTitle: String, + newContent: String, + newFaqType: FaqType, + newAdminId: UUID, + ): Faq { + return copy( + title = newTitle, + content = newContent, + faqType = newFaqType, + adminId = newAdminId, + ) + } +} From bae217946944eb4403448ece66b131054c0b98ec Mon Sep 17 00:00:00 2001 From: coehgns Date: Fri, 1 Aug 2025 17:41:55 +0900 Subject: [PATCH 32/33] feat ( #11 ) : FaqRepository --- .../faq/repository/FaqRepository.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/repository/FaqRepository.kt diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/repository/FaqRepository.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/repository/FaqRepository.kt new file mode 100644 index 0000000..bc8d3d8 --- /dev/null +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/adapter/out/persistence/faq/repository/FaqRepository.kt @@ -0,0 +1,28 @@ +package hs.kr.entrydsm.feed.adapter.out.persistence.faq.repository + +import hs.kr.entrydsm.feed.adapter.out.entity.faq.FaqJpaEntity +import hs.kr.entrydsm.feed.model.faq.type.FaqType +import org.springframework.data.jpa.repository.JpaRepository +import java.util.UUID + +/** + * FAQ 데이터에 접근하기 위한 JPA Repository 인터페이스입니다. + * FAQ 엔티티의 CRUD 및 커스텀 쿼리 메서드를 제공합니다. + */ +interface FaqRepository : JpaRepository { + /** + * 주어진 FAQ 유형에 해당하는 모든 FAQ 엔티티를 조회합니다. + * + * @param faqType 조회할 FAQ 유형 + * @return 조회된 FAQ 엔티티 목록 (없을 경우 빈 목록 반환) + */ + fun findAllByFaqType(faqType: FaqType): List + + /** + * 최근에 생성된 상위 5개의 FAQ 엔티티를 조회합니다. + * 생성일자(createdAt) 기준으로 내림차순 정렬됩니다. + * + * @return 최근 생성된 FAQ 엔티티 목록 (최대 5개, 없을 경우 빈 목록 반환) + */ + fun findTop5ByOrderByCreatedAtDesc(): List +} From 62cead1b2c9dbacb107f0f60e1be0b1eb77f51b9 Mon Sep 17 00:00:00 2001 From: coehgns Date: Sat, 2 Aug 2025 14:41:11 +0900 Subject: [PATCH 33/33] =?UTF-8?q?feat=20(=20#11=20)=20:=20UpdateFaqService?= =?UTF-8?q?=EC=97=90=20@Transactional=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entrydsm/feed/application/faq/service/UpdateFaqService.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt index d5b239c..09385bf 100644 --- a/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt +++ b/casper-feed/src/main/kotlin/hs/kr/entrydsm/feed/application/faq/service/UpdateFaqService.kt @@ -6,6 +6,7 @@ import hs.kr.entrydsm.feed.application.faq.port.`in`.UpdateFaqUseCase import hs.kr.entrydsm.feed.application.faq.port.out.FindFaqPort import hs.kr.entrydsm.feed.global.utils.user.UserUtils import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import java.util.UUID /** @@ -15,6 +16,7 @@ import java.util.UUID * @property userUtils 현재 사용자 정보를 조회하기 위한 유틸리티 */ @Service +@Transactional class UpdateFaqService( private val findFaqPort: FindFaqPort, private val userUtils: UserUtils,