From aed3847b3c7a217b47cce02eb898a946b7c89e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A4=80=ED=99=98?= Date: Sat, 23 Aug 2025 18:52:48 +0900 Subject: [PATCH 1/4] :sparkles: Feature: get detail prompt --- .../prompt/controller/PromptController.kt | 9 +++++++++ .../domain/prompt/dto/PromptResponseDTO.kt | 20 +++++++++++++++++++ .../domain/prompt/service/PromptService.kt | 9 +++++++++ .../ragback/global/error/CustomException.kt | 7 ++++++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt index 368f39f..e16a462 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt @@ -4,6 +4,7 @@ import jakarta.validation.Valid import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.* import simplerag.ragback.domain.prompt.dto.PromptCreateRequest +import simplerag.ragback.domain.prompt.dto.PromptDetailResponse import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse import simplerag.ragback.domain.prompt.service.PromptService import simplerag.ragback.global.response.ApiResponse @@ -23,4 +24,12 @@ class PromptController( return ApiResponse.ok(savedPrompt) } + @GetMapping("/{promptId}") + fun getPrompt( + @PathVariable promptId: Long, + ): ApiResponse { + val promptDetail = promptService.getPrompt(promptId) + return ApiResponse.ok(promptDetail) + } + } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt index 43d73ff..ab47bc6 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt @@ -1,6 +1,11 @@ package simplerag.ragback.domain.prompt.dto +import jakarta.persistence.Column +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.Lob import simplerag.ragback.domain.prompt.entity.Prompt +import simplerag.ragback.domain.prompt.entity.enums.PreSet data class PromptPreviewResponse( val id: Long, @@ -12,4 +17,19 @@ data class PromptPreviewResponse( return PromptPreviewResponse(prompt.id) } } +} + +data class PromptDetailResponse( + val id: Long, + val name: String, + val preSet: PreSet, + val systemPrompt: String, +) { + companion object { + fun from( + prompt: Prompt + ): PromptDetailResponse { + return PromptDetailResponse(prompt.id, prompt.name, prompt.preSet, prompt.systemPrompt) + } + } } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt index acb988d..3ec6624 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt @@ -1,11 +1,15 @@ package simplerag.ragback.domain.prompt.service +import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import simplerag.ragback.domain.prompt.dto.PromptCreateRequest +import simplerag.ragback.domain.prompt.dto.PromptDetailResponse import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse import simplerag.ragback.domain.prompt.entity.Prompt import simplerag.ragback.domain.prompt.repository.PromptRepository +import simplerag.ragback.global.error.ErrorCode +import simplerag.ragback.global.error.PromptException @Service @Transactional(readOnly = true) @@ -21,4 +25,9 @@ class PromptService( val savedPrompt = promptRepository.save(prompt) return PromptPreviewResponse.from(savedPrompt) } + + fun getPrompt(promptId: Long): PromptDetailResponse { + val prompt = promptRepository.findByIdOrNull(promptId) ?: throw PromptException(ErrorCode.NOT_FOUND) + return PromptDetailResponse.from(prompt) + } } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/global/error/CustomException.kt b/src/main/kotlin/simplerag/ragback/global/error/CustomException.kt index c824906..e6ec4ec 100644 --- a/src/main/kotlin/simplerag/ragback/global/error/CustomException.kt +++ b/src/main/kotlin/simplerag/ragback/global/error/CustomException.kt @@ -20,4 +20,9 @@ class FileException( override val errorCode: ErrorCode, override val message: String, override val cause: Throwable? = null, -) : CustomException(errorCode, message, cause) \ No newline at end of file +) : CustomException(errorCode, message, cause) + +class PromptException( + override val errorCode: ErrorCode, + override val cause: Throwable? = null, +) : CustomException(errorCode, errorCode.message, cause) From cdd713f803fe2da4153d6374147e90d774be388a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A4=80=ED=99=98?= Date: Sat, 23 Aug 2025 19:00:36 +0900 Subject: [PATCH 2/4] :sparkles: Feature: get prompt --- .../prompt/controller/PromptController.kt | 10 ++++++++ .../domain/prompt/dto/PromptResponseDTO.kt | 24 +++++++++++++++---- .../prompt/repository/PromptRepository.kt | 6 ++++- .../domain/prompt/service/PromptService.kt | 15 ++++++++++++ 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt index e16a462..3e63ff5 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.* import simplerag.ragback.domain.prompt.dto.PromptCreateRequest import simplerag.ragback.domain.prompt.dto.PromptDetailResponse import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse +import simplerag.ragback.domain.prompt.dto.PromptPreviewResponseList import simplerag.ragback.domain.prompt.service.PromptService import simplerag.ragback.global.response.ApiResponse @@ -32,4 +33,13 @@ class PromptController( return ApiResponse.ok(promptDetail) } + @GetMapping + fun getPrompts( + @RequestParam cursor: Long, + @RequestParam take: Int, + ): ApiResponse { + val promptPreviewResponseList = promptService.getPrompts(cursor, take) + return ApiResponse.ok(promptPreviewResponseList) + } + } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt index ab47bc6..3ad6036 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt @@ -1,20 +1,34 @@ package simplerag.ragback.domain.prompt.dto -import jakarta.persistence.Column -import jakarta.persistence.EnumType -import jakarta.persistence.Enumerated -import jakarta.persistence.Lob import simplerag.ragback.domain.prompt.entity.Prompt import simplerag.ragback.domain.prompt.entity.enums.PreSet +data class PromptPreviewResponseList( + val promptPreviewResponseList: List, + val cursor: Long?, + val hasNext: Boolean +) { + companion object { + fun from(prompts: List, cursor: Long?, hasNext: Boolean): PromptPreviewResponseList = + PromptPreviewResponseList( + promptPreviewResponseList = prompts.map { prompt -> + PromptPreviewResponse.from(prompt) + }, + cursor = cursor, + hasNext = hasNext, + ) + } +} + data class PromptPreviewResponse( val id: Long, + val name: String, ) { companion object { fun from( prompt: Prompt ): PromptPreviewResponse { - return PromptPreviewResponse(prompt.id) + return PromptPreviewResponse(prompt.id, prompt.name) } } } diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/repository/PromptRepository.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/repository/PromptRepository.kt index fb893ff..9a0d21c 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/repository/PromptRepository.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/repository/PromptRepository.kt @@ -1,6 +1,10 @@ package simplerag.ragback.domain.prompt.repository +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice import org.springframework.data.jpa.repository.JpaRepository import simplerag.ragback.domain.prompt.entity.Prompt -interface PromptRepository: JpaRepository \ No newline at end of file +interface PromptRepository: JpaRepository { + fun findByIdGreaterThanOrderById(cursor: Long, pageable: Pageable): Slice +} \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt index 3ec6624..a60dd75 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt @@ -1,11 +1,15 @@ package simplerag.ragback.domain.prompt.service +import org.springframework.data.domain.PageRequest import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional +import simplerag.ragback.domain.document.dto.DataFileDetailResponseList +import simplerag.ragback.domain.document.dto.TagDTO import simplerag.ragback.domain.prompt.dto.PromptCreateRequest import simplerag.ragback.domain.prompt.dto.PromptDetailResponse import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse +import simplerag.ragback.domain.prompt.dto.PromptPreviewResponseList import simplerag.ragback.domain.prompt.entity.Prompt import simplerag.ragback.domain.prompt.repository.PromptRepository import simplerag.ragback.global.error.ErrorCode @@ -30,4 +34,15 @@ class PromptService( val prompt = promptRepository.findByIdOrNull(promptId) ?: throw PromptException(ErrorCode.NOT_FOUND) return PromptDetailResponse.from(prompt) } + + fun getPrompts( + cursor: Long, + take: Int + ): PromptPreviewResponseList { + val prompts = promptRepository.findByIdGreaterThanOrderById(cursor, PageRequest.of(0, take)) + + val nextCursor = prompts.content.lastOrNull()?.id + + return PromptPreviewResponseList.from(prompts.content, nextCursor, prompts.hasNext()) + } } \ No newline at end of file From f9f45b9737448eeb31b92619b9ed80126a863fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A4=80=ED=99=98?= Date: Sat, 23 Aug 2025 23:24:34 +0900 Subject: [PATCH 3/4] :sparkles: Feature: update prompt --- .../prompt/controller/PromptController.kt | 15 ++++-- .../domain/prompt/dto/PromptRequestDTO.kt | 10 +++- .../ragback/domain/prompt/entity/Prompt.kt | 53 +++++++++++++------ .../domain/prompt/service/PromptService.kt | 15 ++++-- .../ragback/global/error/ErrorCode.kt | 5 +- 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt index 3e63ff5..e57859a 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt @@ -3,10 +3,8 @@ package simplerag.ragback.domain.prompt.controller import jakarta.validation.Valid import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.* -import simplerag.ragback.domain.prompt.dto.PromptCreateRequest -import simplerag.ragback.domain.prompt.dto.PromptDetailResponse -import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse -import simplerag.ragback.domain.prompt.dto.PromptPreviewResponseList +import simplerag.ragback.domain.index.dto.IndexUpdateRequest +import simplerag.ragback.domain.prompt.dto.* import simplerag.ragback.domain.prompt.service.PromptService import simplerag.ragback.global.response.ApiResponse @@ -42,4 +40,13 @@ class PromptController( return ApiResponse.ok(promptPreviewResponseList) } + @PutMapping("/{promptId}") + fun updatePrompt( + @RequestBody @Valid promptUpdateRequest: PromptUpdateRequest, + @PathVariable promptId: Long, + ): ApiResponse { + val updatedPrompt = promptService.updatePrompt(promptUpdateRequest, promptId) + return ApiResponse.ok(updatedPrompt) + } + } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptRequestDTO.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptRequestDTO.kt index 95ea0b1..7e9864a 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptRequestDTO.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptRequestDTO.kt @@ -9,5 +9,13 @@ data class PromptCreateRequest( val name: String, val preSet: PreSet, @field:Size(max = 20000) - val systemPrompt: String, + val systemPrompt: String?, +) + +data class PromptUpdateRequest( + @field:Size(max = 100) + val name: String, + val preSet: PreSet, + @field:Size(max = 20000) + val systemPrompt: String?, ) \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt index 637d652..2f7476d 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt @@ -3,22 +3,25 @@ package simplerag.ragback.domain.prompt.entity import simplerag.ragback.domain.prompt.entity.enums.PreSet import jakarta.persistence.* import simplerag.ragback.domain.prompt.dto.PromptCreateRequest +import simplerag.ragback.domain.prompt.dto.PromptUpdateRequest import simplerag.ragback.global.entity.BaseEntity +import simplerag.ragback.global.error.ErrorCode +import simplerag.ragback.global.error.PromptException @Entity @Table(name = "prompts") class Prompt( @Column(name = "name", length = 100, nullable = false) - val name: String, + var name: String, @Enumerated(EnumType.STRING) @Column(name = "pre_set", nullable = false) - val preSet: PreSet, + var preSet: PreSet, @Column(name = "system_prompt", nullable = false) @Lob - val systemPrompt: String, + var systemPrompt: String, ) : BaseEntity() { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -26,25 +29,41 @@ class Prompt( val id: Long = 0 companion object { - fun from( - promptCreateRequest: PromptCreateRequest - ): Prompt { - - val prompt = if (promptCreateRequest.preSet == PreSet.CUSTOM) { - Prompt( - promptCreateRequest.name, - promptCreateRequest.preSet, - promptCreateRequest.systemPrompt - ) - } else { - Prompt( + fun from(promptCreateRequest: PromptCreateRequest): Prompt = + when (promptCreateRequest.preSet) { + PreSet.CUSTOM -> Prompt( promptCreateRequest.name, promptCreateRequest.preSet, - promptCreateRequest.preSet.defaultSystemPrompt + promptCreateRequest.systemPrompt ?: "" ) + else -> { + if(!promptCreateRequest.systemPrompt.isNullOrBlank()) { + throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT) + } + Prompt( + promptCreateRequest.name, + promptCreateRequest.preSet, + promptCreateRequest.preSet.defaultSystemPrompt + ) + } } + } - return prompt + fun update(promptUpdateRequest: PromptUpdateRequest) { + when (promptUpdateRequest.preSet) { + PreSet.CUSTOM -> { + this.name = promptUpdateRequest.name + this.systemPrompt = promptUpdateRequest.systemPrompt ?: "" + this.preSet = promptUpdateRequest.preSet + } + else -> { + if(!promptUpdateRequest.systemPrompt.isNullOrBlank()) { + throw PromptException(ErrorCode.CUSTOM_SYSTEM_PROMPT) + } + this.name = promptUpdateRequest.name + this.systemPrompt = promptUpdateRequest.preSet.defaultSystemPrompt + this.preSet = promptUpdateRequest.preSet + } } } } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt index a60dd75..f85ed61 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt @@ -6,10 +6,7 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import simplerag.ragback.domain.document.dto.DataFileDetailResponseList import simplerag.ragback.domain.document.dto.TagDTO -import simplerag.ragback.domain.prompt.dto.PromptCreateRequest -import simplerag.ragback.domain.prompt.dto.PromptDetailResponse -import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse -import simplerag.ragback.domain.prompt.dto.PromptPreviewResponseList +import simplerag.ragback.domain.prompt.dto.* import simplerag.ragback.domain.prompt.entity.Prompt import simplerag.ragback.domain.prompt.repository.PromptRepository import simplerag.ragback.global.error.ErrorCode @@ -45,4 +42,14 @@ class PromptService( return PromptPreviewResponseList.from(prompts.content, nextCursor, prompts.hasNext()) } + + @Transactional + fun updatePrompt( + promptUpdateRequest: PromptUpdateRequest, + promptId: Long + ): PromptPreviewResponse { + val prompt = promptRepository.findByIdOrNull(promptId) ?: throw PromptException(ErrorCode.NOT_FOUND) + prompt.update(promptUpdateRequest) + return PromptPreviewResponse.from(prompt) + } } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt b/src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt index 308a7ed..65ff8e5 100644 --- a/src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt +++ b/src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt @@ -25,5 +25,8 @@ enum class ErrorCode( S3_UNSUPPORTED_CONTENT_TYPE(HttpStatus.BAD_REQUEST, "S3_007", "지원하지 않는 Content-Type 입니다."), // index - OVERLAP_OVERFLOW(HttpStatus.BAD_REQUEST, "INDEX_001", "overlap 크기는 chunking 크기를 넘을 수 없습니다.") + OVERLAP_OVERFLOW(HttpStatus.BAD_REQUEST, "INDEX_001", "overlap 크기는 chunking 크기를 넘을 수 없습니다."), + + // prompt + CUSTOM_SYSTEM_PROMPT(HttpStatus.BAD_REQUEST, "PROMPT_001", "preset이 Custom이 아닐 때 systemPrompt의 조작이 불가능합니다.") } From 5d95642083a6fc42a79d77e63b54fea816e55cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=A4=80=ED=99=98?= Date: Sat, 23 Aug 2025 23:26:23 +0900 Subject: [PATCH 4/4] :sparkles: Feature: delete prompt --- .../ragback/domain/prompt/controller/PromptController.kt | 8 ++++++++ .../ragback/domain/prompt/service/PromptService.kt | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt index e57859a..188ed31 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt @@ -49,4 +49,12 @@ class PromptController( return ApiResponse.ok(updatedPrompt) } + @DeleteMapping("/{promptId}") + fun deletePrompt( + @PathVariable promptId: Long, + ): ApiResponse { + promptService.deletePrompt(promptId) + return ApiResponse.ok(null, "프롬프트가 삭제되었습니다.") + } + } \ No newline at end of file diff --git a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt index f85ed61..ed28d81 100644 --- a/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt +++ b/src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt @@ -52,4 +52,10 @@ class PromptService( prompt.update(promptUpdateRequest) return PromptPreviewResponse.from(prompt) } + + @Transactional + fun deletePrompt(promptId: Long) { + val prompt = promptRepository.findByIdOrNull(promptId) ?: throw PromptException(ErrorCode.NOT_FOUND) + promptRepository.delete(prompt) + } } \ No newline at end of file