Skip to content

Commit

Permalink
Merge pull request #109 from Kusitms-29th-ASAP/feature/tranlate/todo
Browse files Browse the repository at this point in the history
feat: 투두 번역 및 변역 결과 저장 테이블 추가
  • Loading branch information
tlarbals824 authored Aug 8, 2024
2 parents 4113f83 + 8bbae30 commit 16a9291
Show file tree
Hide file tree
Showing 25 changed files with 631 additions and 80 deletions.
17 changes: 17 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ repositories {

val kotestVersion = "5.8.1"
val mockkVersion = "1.13.10"
extra["springModulithVersion"] = "1.2.0"


val asciidoctorExt = "asciidoctorExt"
Expand Down Expand Up @@ -82,15 +83,31 @@ dependencies {
testFixturesImplementation("org.springframework.restdocs:spring-restdocs-mockmvc")

// webclient
// if(System.getProperty("os.name") == "Mac OS X" && System.getProperty("os.arch") == "aarch64")
runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.109.Final:osx-aarch_64")
implementation("org.springframework.boot:spring-boot-starter-webflux")


// logger
implementation("io.github.oshai:kotlin-logging-jvm:5.1.0")

// kotlin coroutine
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.8.0")


// spring modulith
implementation("org.springframework.modulith:spring-modulith-starter-core")
implementation("org.springframework.modulith:spring-modulith-starter-jpa")

}



dependencyManagement {
imports {
mavenBom("org.springframework.modulith:spring-modulith-bom:${property("springModulithVersion")}")
}
}

tasks.withType<KotlinCompile> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.asap.asapbackend.batch.event

import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.modulith.events.IncompleteEventPublications
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component

private val logger = KotlinLogging.logger { }

@Component
class IncompleteEventRepublicationHandler(
private val applicationEventMulticaster: IncompleteEventPublications
) {

// 매 분마다 실행
@Scheduled(cron = "0 0 * * * ?")
fun resubmitIncompletePublications() {
applicationEventMulticaster.resubmitIncompletePublications { true }
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.asap.asapbackend.client.openai.chatgpt

import com.asap.asapbackend.client.openai.OpenAIProperties
import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.client.WebClient

@Component
class GptClient(
private val openAIProperties: OpenAIProperties
) {

fun getResultForContentWithPolicy(
content: String,
policy: Policy
): String? {
val response = WebClient.builder()
.baseUrl("https://api.openai.com")
.build()
.post()
.uri("/v1/chat/completions")
.header("Authorization", "Bearer ${openAIProperties.apiKey}")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(
mapOf(
"model" to "gpt-4o-mini",
"messages" to listOf(
mapOf(
"role" to "system",
"content" to policy.instruction
),
mapOf(
"role" to "user",
"content" to policy.exampleContent
),
mapOf(
"role" to "assistant",
"content" to policy.exampleResult
),
mapOf(
"role" to "user",
"content" to content
)
)
)
)
.retrieve()
.bodyToMono(GptResponse::class.java)
.block()
return response?.let {
it.getMessage(0).also { message ->
if (policy.isJson)
convertJsonFormat(message)
}
}
}

private fun convertJsonFormat(content: String): String {
return content.replace("```json", "").replace("```", "")
}

data class Policy(
val instruction: String,
val exampleContent: String,
val exampleResult: String,
val isJson: Boolean
)

data class GptResponse(
val id: String,
val model: String,
val choices: List<Choice>
) {
fun getMessage(index: Int): String {
return choices[index].message.content
}
}

data class Choice(
val index: Int,
val message: Message
)

data class Message(
val role: String,
val content: String
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.asap.asapbackend.client.openai.chatgpt

import com.asap.asapbackend.global.util.TextTranslator
import com.asap.asapbackend.global.vo.Language
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.springframework.stereotype.Component

@Component
class GptTextTranslateClient(
private val gptClient: GptClient,
private val objectMapper: ObjectMapper
) : TextTranslator {
override fun translate(text: String): TextTranslator.TranslateResponse{
val policy = GptClient.Policy(
GptTextTranslatePrompt.TRANSLATION_INSTRUCTION,
GptTextTranslatePrompt.EXAMPLE_CONTENT,
GptTextTranslatePrompt.TRANSLATION_RESPONSE,
true
)
val result = gptClient.getResultForContentWithPolicy(text, policy) ?: run {
throw IllegalStateException("Failed to get result from GPT")
}

return objectMapper.readValue(result)
}


private object GptTextTranslatePrompt {
val TRANSLATION_INSTRUCTION = """
위 문장을 ${Language.getAllLanguageNames()} 언어로 번역해줘, 변역을 완료하면 JSON 형태로 출력해줘.
각 언어를 번역할때는 언어에 맞는 key와 value로 출력해줘. 예시 형태는 다음과 같아.
"""

val TRANSLATIONS = Language.entries.joinToString(", ") { language ->
"""{ "language" : "${language.name}", "text" : "String" }"""
}

val TRANSLATION_RESPONSE = """
{
"translations" : [
$TRANSLATIONS
]
}
"""

val EXAMPLE_CONTENT = """
{
"translations" : [
{ "language" : "KOREAN", "text" : "안녕하세요" },
{ "language" : "ENGLISH", "text" : "Hello" },
{ "language" : "CHINESE", "text" : "你好" },
{ "language" : "JAPANESE", "text" : "こんにちは" },
{ "language" : "VIETNAMESE", "text" : "Xin chào" },
{ "language" : "PILIPINO", "text" : "Kamusta" }
]
}
""".trimIndent()


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CreateClassroomAnnouncement {
}.flatMap { // 30*4 = 120
students.map { student ->
Todo(
child = student,
childId = student.id,
description = it.description,
type = it.todoType,
deadline = it.deadline,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class TodoService(
val todo = Todo(
type = request.todoType,
description = request.description,
child = child,
childId = child.id,
deadline = request.deadline,
isAssigned = false
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
package com.asap.asapbackend.domain.todo.domain.model

import com.asap.asapbackend.domain.child.domain.model.Child
import com.asap.asapbackend.domain.todo.domain.vo.Status
import com.asap.asapbackend.domain.todo.domain.vo.TodoType
import com.asap.asapbackend.global.domain.BaseDateEntity
import com.asap.asapbackend.global.exception.validateProperty
import jakarta.persistence.*
import java.time.LocalDate
import java.time.LocalDateTime


@Entity
class Todo (
id: Long = 0L,
type: TodoType,
description: String,
child: Child,
childId: Long,
deadline: LocalDate,
isAssigned: Boolean
):BaseDateEntity(){
isAssigned: Boolean,
createdAt: LocalDateTime = LocalDateTime.now(),
updatedAt: LocalDateTime = LocalDateTime.now()
){

init {
validateProperty(description.isNotBlank())
validateProperty(deadline.isAfter(LocalDate.now().minusDays(1)))
}

@Enumerated(EnumType.STRING)
@Column(
nullable = false,
columnDefinition = "varchar(255)"
)
val id: Long = id

val type: TodoType = type

val description : String = description
Expand All @@ -35,16 +33,13 @@ class Todo (

val isAssigned : Boolean = isAssigned

@Enumerated(EnumType.STRING)
@Column(
nullable = false,
columnDefinition = "varchar(255)"
)
var status: Status = Status.INCOMPLETE
protected set

@ManyToOne(fetch = FetchType.LAZY)
val child : Child = child
val childId : Long = childId

val createdAt : LocalDateTime = createdAt
var updatedAt : LocalDateTime = updatedAt

fun changeStatus(){
status = if(status == Status.COMPLETE) Status.INCOMPLETE else Status.COMPLETE
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
package com.asap.asapbackend.domain.todo.domain.repository

import com.asap.asapbackend.domain.todo.domain.model.Todo
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import java.time.LocalDate

interface TodoRepository : JpaRepository<Todo, Long> {
interface TodoRepository {
fun findAllByChildIdAndDeadlineAfter(childId: Long, deadline: LocalDate): List<Todo>

@Query("""
select t
from Todo t
join fetch t.child
join PrimaryChild p on p.childId = t.child.id and p.userId = :userId
""")
fun findAllByUserId(userId: Long): List<Todo>
fun findByUserIdAndTodoId(userId: Long, todoId: Long): Todo?

fun save(todo: Todo): Todo

@Query("""
select t
from Todo t
join t.child c on c.id = t.child.id and c.parent.id = :userId
where t.id = :todoId
""")
fun findByUserIdAndTodoId(userId: Long, todoId: Long): Todo?
fun insertBatch(todos: Set<Todo>)

fun delete(todo: Todo)
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
package com.asap.asapbackend.domain.todo.domain.service

import com.asap.asapbackend.domain.todo.domain.model.Todo
import com.asap.asapbackend.domain.todo.domain.repository.TodoJdbcRepository
import com.asap.asapbackend.domain.todo.domain.repository.TodoRepository
import com.asap.asapbackend.domain.todo.event.MultiTodoCreateEvent
import com.asap.asapbackend.domain.todo.event.TodoCreateEvent
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service

@Service
class TodoAppender(
private val todoRepository: TodoRepository,
private val todoJdbcRepository: TodoJdbcRepository
private val applicationEventPublisher: ApplicationEventPublisher
) {

fun appendAll(todos: Set<Todo>) {
todoRepository.saveAll(todos)
}

fun appendAllBatch(todos: Set<Todo>) {
todoJdbcRepository.insertBatch(todos)
todoRepository.insertBatch(todos)
applicationEventPublisher.publishEvent(MultiTodoCreateEvent(todos))
}

fun appendTodo(todo: Todo): Todo {
return todoRepository.save(todo)
val createdTodo = todoRepository.save(todo)
applicationEventPublisher.publishEvent(TodoCreateEvent(createdTodo))
return createdTodo
}
}
Loading

0 comments on commit 16a9291

Please sign in to comment.