Skip to content

Commit

Permalink
[Refactor/#143] 어드민 API 요구사항 반영 및 오류 수정 (#144)
Browse files Browse the repository at this point in the history
* refactor: 아티클 등록시 여러 개의 문제를 함께 등록할 수 있도록 수정

* refactor: 카테고리 정책 변경 반영

* refactor: html 태그 정책 수정 반영

* refactor: contentType을 통해 md와 html contentSource를 처리하도록 수정

* feat: api-start 스크립트에 pull 단계 추가

* refactor: html article 테그 추가 반영

* refactor: html 테그 클래스 지정 반영

* refactor: img 태그 style 속성 지정

* refactor: html 테그 스타일 지정

* refactor: article.html 수정

* refactor: table 스타일 변경 반영

* refactor: a 테그 스타일 추가 반영

* refactor: problemData -> problems
  • Loading branch information
belljun3395 authored Jul 8, 2024
1 parent 606b325 commit 363318f
Show file tree
Hide file tree
Showing 12 changed files with 300 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,17 @@ class ProblemDao(
.let { ProblemIdsRecord(it) }
}

fun insertProblems(command: InsertProblemsCommand) {
dslContext.insertInto(Problem.PROBLEM)
.set(Problem.PROBLEM.ARTICLE_ID, command.articleId)
.set(Problem.PROBLEM.CREATOR_ID, command.createrId)
.set(Problem.PROBLEM.TITLE, command.title)
.set(Problem.PROBLEM.CONTENTS, JSON.valueOf(contentsJsonMapper.toJson(command.contents)))
.set(Problem.PROBLEM.ANSWER, command.answer)
.set(Problem.PROBLEM.EXPLANATION, command.explanation)
.execute()
fun insertProblems(command: List<InsertProblemsCommand>) {
dslContext.batch(
command.map {
dslContext.insertInto(Problem.PROBLEM)
.set(Problem.PROBLEM.ARTICLE_ID, it.articleId)
.set(Problem.PROBLEM.CREATOR_ID, it.createrId)
.set(Problem.PROBLEM.TITLE, it.title)
.set(Problem.PROBLEM.CONTENTS, JSON.valueOf(contentsJsonMapper.toJson(it.contents)))
.set(Problem.PROBLEM.ANSWER, it.answer)
.set(Problem.PROBLEM.EXPLANATION, it.explanation)
}
).execute()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ data class AddArticleUseCaseIn(
val title: String,
val category: String,
/** Article IFO */
val contentType: String,
val contentSource: String,
val problemData: ProblemDetail
val problems: List<ProblemDetail>

)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,36 @@ package com.few.api.domain.admin.document.usecase

import com.few.api.domain.admin.document.dto.AddArticleUseCaseIn
import com.few.api.domain.admin.document.dto.AddArticleUseCaseOut
import com.few.api.domain.admin.document.service.GetUrlService
import com.few.api.domain.admin.document.service.dto.GetUrlQuery
import com.few.api.domain.admin.document.utils.ObjectPathGenerator
import com.few.api.repo.dao.article.ArticleDao
import com.few.api.repo.dao.article.command.InsertFullArticleRecordCommand
import com.few.api.repo.dao.document.DocumentDao
import com.few.api.repo.dao.document.command.InsertDocumentIfoCommand
import com.few.api.repo.dao.member.MemberDao
import com.few.api.repo.dao.member.query.SelectMemberByEmailQuery
import com.few.api.repo.dao.problem.ProblemDao
import com.few.api.repo.dao.problem.command.InsertProblemsCommand
import com.few.api.repo.dao.problem.support.Content
import com.few.api.repo.dao.problem.support.Contents
import com.few.data.common.code.CategoryType
import com.few.storage.document.service.ConvertDocumentService
import com.few.storage.document.service.PutDocumentService
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import java.io.File
import java.util.*

@Component
class AddArticleUseCase(
private val articleDao: ArticleDao,
private val memberDao: MemberDao,
private val problemDao: ProblemDao
private val problemDao: ProblemDao,
private val documentDao: DocumentDao,
private val convertDocumentService: ConvertDocumentService,
private val putDocumentService: PutDocumentService,
private val getUrlService: GetUrlService
) {
@Transactional
fun execute(useCaseIn: AddArticleUseCaseIn): AddArticleUseCaseOut {
Expand All @@ -27,32 +40,78 @@ class AddArticleUseCase(
memberDao.selectMemberByEmail(it)
} ?: throw RuntimeException("writer not found")

/**
* - content type: "md"
* put origin document to object storage
* and convert to html source
* - content type: "html"
* save html source
*/
val htmlSource = when {
useCaseIn.contentType.lowercase(Locale.getDefault()) == "md" -> {
val mdSource = useCaseIn.contentSource
val htmlSource = convertDocumentService.mdToHtml(useCaseIn.contentSource)

val document = runCatching {
File.createTempFile("temp", ".md")
}.onSuccess {
it.writeText(mdSource)
}.getOrThrow()
val documentName = ObjectPathGenerator.documentPath("md")

putDocumentService.execute(documentName, document)?.let { res ->
val source = res.`object`
GetUrlQuery(source).let { query ->
getUrlService.execute(query)
}.let { url ->
InsertDocumentIfoCommand(
path = documentName,
url = url
).let { command ->
documentDao.insertDocumentIfo(command)
}
url
}
} ?: throw IllegalStateException("Failed to put document")

htmlSource
}
useCaseIn.contentType.lowercase(Locale.getDefault()) == "html" -> {
useCaseIn.contentSource
}
else -> {
throw IllegalArgumentException("content type is not supported")
}
}

/** insert article */
val articleMstId = InsertFullArticleRecordCommand(
writerId = writerId.memberId,
mainImageURL = useCaseIn.articleImageUrl,
title = useCaseIn.title,
category = CategoryType.convertToCode(useCaseIn.category),
content = useCaseIn.contentSource
content = htmlSource
).let { articleDao.insertFullArticleRecord(it) }

/** insert problems */
InsertProblemsCommand(
articleId = articleMstId,
createrId = 0L, // todo fix
title = useCaseIn.title,
contents = Contents(
useCaseIn.problemData.contents.map {
Content(
it.number,
it.content
)
}
),
answer = useCaseIn.problemData.answer,
explanation = useCaseIn.problemData.explanation
).let {
problemDao.insertProblems(it)
useCaseIn.problems.stream().map { problemDatum ->
InsertProblemsCommand(
articleId = articleMstId,
createrId = 0L, // todo fix
title = problemDatum.title,
contents = Contents(
problemDatum.contents.map { detail ->
Content(
detail.number,
detail.content
)
}
),
answer = problemDatum.answer,
explanation = problemDatum.explanation
)
}.toList().let { commands ->
problemDao.insertProblems(commands)
}

return AddArticleUseCaseOut(articleMstId)
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/kotlin/com/few/api/web/config/WebConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
@EnableWebMvc
@EnableWebMvc // todo refactor
class WebConfig : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,21 @@ class AdminController(
articleImageUrl = request.articleImageUrl,
title = request.title,
category = request.category,
contentType = request.contentType,
contentSource = request.contentSource,
problemData = ProblemDetail(
title = request.problemData.title,
contents = request.problemData.contents.map {
ProblemContentDetail(
number = it.number,
content = it.content
)
},
answer = request.problemData.answer,
explanation = request.problemData.explanation
)
problems = request.problemData.map { datum ->
ProblemDetail(
title = datum.title,
contents = datum.contents.map { detail ->
ProblemContentDetail(
number = detail.number,
content = detail.content
)
},
answer = datum.answer,
explanation = datum.explanation
)
}.toList()
).let { useCaseIn ->
addArticleUseCase.execute(useCaseIn)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ data class AddArticleRequest(
@field:NotBlank(message = "{category.notblank}")
val category: String,
/** Article IFO */
@NotBlank(message = "{content.type.notblank}")
val contentType: String,
@field:NotBlank(message = "{content.source.notblank}")
val contentSource: String,
val problemData: ProblemDto
val problemData: List<ProblemDto>
)

data class ProblemDto(
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/resources/ValidationMessages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ image.url.notblank=Image URL must be blank
title.notblank=Title must not be blank
category.notblank=Category must not be blank
content.source.notblank=Content must not be blank
content.type.notblank=Content Type must not be blank
problem.title.notblank=Problem Title must not be blank
problem.content.notblank=Problem Content must not be blank
problem.answer.notblank=Problem Answer must not be blank
Expand All @@ -14,4 +15,3 @@ workbook.description.notblank=Workbook Description must not be blank
min.day=The Day field must be greater than or equal to 1
min.id=The ID field must be greater than or equal to 1
min.problem.number=The Problem Number field must be greater than or equal to 1
max.day=The Day field must be less than or equal to 7
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,31 @@ class AdminControllerTest : ControllerTestSpec() {
URL("http://localhost:8080"),
"title",
CategoryType.fromCode(0)!!.name,
"contentSource",
ProblemDto(
"title",
listOf(
ProblemContentDto(1L, "content1"),
ProblemContentDto(2L, "content2"),
ProblemContentDto(3L, "content3"),
ProblemContentDto(4L, "content4")
"md",
"content source",
listOf(
ProblemDto(
"title1",
listOf(
ProblemContentDto(1L, "content1"),
ProblemContentDto(2L, "content2"),
ProblemContentDto(3L, "content3"),
ProblemContentDto(4L, "content4")
),
"1",
"explanation"
),
"1",
"explanation"
ProblemDto(
"title2",
listOf(
ProblemContentDto(1L, "content1"),
ProblemContentDto(2L, "content2"),
ProblemContentDto(3L, "content3"),
ProblemContentDto(4L, "content4")
),
"2",
"explanation"
)
)
)
val body = objectMapper.writeValueAsString(request)
Expand All @@ -159,17 +173,31 @@ class AdminControllerTest : ControllerTestSpec() {
URL("http://localhost:8080"),
"title",
CategoryType.fromCode(0)!!.name,
"contentSource",
ProblemDetail(
"title",
listOf(
ProblemContentDetail(1L, "content1"),
ProblemContentDetail(2L, "content2"),
ProblemContentDetail(3L, "content3"),
ProblemContentDetail(4L, "content4")
"md",
"content source",
listOf(
ProblemDetail(
"title1",
listOf(
ProblemContentDetail(1L, "content1"),
ProblemContentDetail(2L, "content2"),
ProblemContentDetail(3L, "content3"),
ProblemContentDetail(4L, "content4")
),
"1",
"explanation"
),
"1",
"explanation"
ProblemDetail(
"title2",
listOf(
ProblemContentDetail(1L, "content1"),
ProblemContentDetail(2L, "content2"),
ProblemContentDetail(3L, "content3"),
ProblemContentDetail(4L, "content4")
),
"2",
"explanation"
)
)
)
)
Expand Down
16 changes: 5 additions & 11 deletions data/src/main/kotlin/com/few/data/common/code/CategoryType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@ package com.few.data.common.code
* @see com.few.batch.data.common.code.BatchCategoryType
*/
enum class CategoryType(val code: Byte, val displayName: String) {
POLITICS(0, "정치"),
ECONOMY(10, "경제"),
SOCIETY(20, "사회"),
CULTURE(30, "문화"),
LIFE(40, "생활"),
IT(50, "IT"),
SCIENCE(60, "과학"),
ENTERTAINMENTS(70, "엔터테인먼트"),
SPORTS(80, "스포츠"),
GLOBAL(90, "국제"),
ETC(100, "기타");
ECONOMY(0, "경제"),
IT(10, "IT"),
MARKETING(20, "마케팅"),
CULTURE(30, "교양"),
SCIENCE(40, "과학");

companion object {
fun fromCode(code: Byte): CategoryType? {
Expand Down
Loading

0 comments on commit 363318f

Please sign in to comment.