Skip to content

Commit

Permalink
[#6] 게시글 생성 및 조회 API Merge (#30)
Browse files Browse the repository at this point in the history
* feat: community 글 생성 및 조회 api 구현

* test: article 생성, 조회 테스트

* refactor: PR리뷰 반영(path 수정, fetch type 수정, NotNull 삭제 등)

* refactor: test code 수정

* refactor: dto 수정 및 그에 따른 service 수정

* test: article get, post controller test

* fix: Article topic 추가

* fix: Topic enum 수정, ArticleControllerTest MockMvc 사용하여 수정

* fix: pr리뷰 반영(http status code 삭제, dto에 Topic enum 받도록 수정 등)

* fix: article 작성 요청 dto 이름 수정, NO_NICKNAME 에러코드 추가

* docs: swagger 작성

* refactor: NO_NICKNAME -> NICKNAME_REQUIRED로 수정, ARTICLE_NOT_FOUND 에러 추가

* refactor: article write request dto 이름 수정

* refactor: cherry-pick 완료, controller test에서 토큰 사용

* refactor: 필요없는 Transactional 어노테이션 삭제
  • Loading branch information
CreatePath authored Aug 17, 2023
1 parent e5dc894 commit d09ec5d
Show file tree
Hide file tree
Showing 16 changed files with 493 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,4 @@ class RequestLoggingFilter : OncePerRequestFilter() {
}
return jacksonObjectMapper().writeValueAsString(jsonMap)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,4 @@ class SwaggerConfiguration {
&nbsp; }' <br />
</code>
""".trimIndent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ private const val SERVER_NUMBERING = 4000
enum class ErrorCode(val code: Int, val message: String) {
USER_NOT_FOUND(MAIN_NUMBERING + 1, "유저를 찾을 수 없습니다"),
DEPARTMENT_NOT_FOUND(MAIN_NUMBERING + 2, "학과를 찾을 수 없습니다"),
ARTICLE_NOT_FOUND(MAIN_NUMBERING + 3, "게시글을 찾을 수 없습니다."),
NICKNAME_REQUIRED(MAIN_NUMBERING + 4, "닉네임이 필요한 작업입니다."),

COURSE_NOT_FOUND(COURSE_NUMBERING + 1, "과목을 찾을 수 없습니다."),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.example.daitssuapi.domain.main.controller

import com.example.daitssuapi.common.dto.Response
import com.example.daitssuapi.domain.main.dto.response.ArticleResponse
import com.example.daitssuapi.domain.main.dto.request.ArticleWriteRequest
import com.example.daitssuapi.domain.main.service.ArticleService
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/daitssu/community/article")
@Tag(name = "article", description = "커뮤니티 게시글 API")
class ArticleController(
private val articleService: ArticleService
) {
@Operation(
summary = "게시글 단일 조회",
responses = [
ApiResponse(
responseCode = "200",
description = "OK"
)
]
)
@GetMapping("/{articleId}")
fun getArticle(
@Parameter(name = "articleId", description = "게시글 id")
@PathVariable articleId: Long
): Response<ArticleResponse>
= Response(data = articleService.getArticle(articleId))

@Operation(
summary = "새로운 게시글 작성",
responses = [
ApiResponse(
responseCode = "200",
description = "OK"
)
]
)
@PostMapping
fun writeArticle(
@RequestBody articleWriteRequest: ArticleWriteRequest
): Response<ArticleResponse>
= Response(data = articleService.writeArticle(articleWriteRequest))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.daitssuapi.domain.main.dto.request

import com.example.daitssuapi.domain.main.enums.Topic
import io.swagger.v3.oas.annotations.media.Schema

@Schema(description = "커뮤니티 게시글 작성 API request body")
data class ArticleWriteRequest(
@Schema(
description = "게시글 주제",
allowableValues = ["CHAT", "INFORMATION", "QUESTION"]
)
val topic: Topic,

@Schema(description = "게시글 제목")
val title: String,

@Schema(description = "게시글 내용")
val content: String,

@Schema(description = "작성자 닉네임")
val nickname: String? = null
// val studentId: Int,
// val images, // image 데이터
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.daitssuapi.domain.main.dto.response

import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDateTime

@Schema(description = "단일 게시글 정보")
data class ArticleResponse(
@Schema(description = "게시글 id")
val id: Long,

@Schema(
description = "게시글 주제",
allowableValues = ["잡담", "정보", "질문"]
)
val topic: String,

@Schema(description = "게시글 제목")
val title: String,

@Schema(description = "게시글 내용")
val content: String,

@Schema(description = "작성자 닉네임")
val writerNickName: String,

@Schema(description = "마지막 수정된 시각")
val updatedAt: LocalDateTime,

// val images // image 데이터
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.daitssuapi.domain.main.enums

enum class Topic(val value: String) {
CHAT("잡담"),
QUESTION("질문"),
INFORMATION("정보");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.daitssuapi.domain.main.model.entity

import com.example.daitssuapi.common.audit.BaseEntity
import com.example.daitssuapi.domain.main.enums.Topic
import jakarta.persistence.*

@Entity
@Table(schema = "main")
class Article(
@Enumerated(value = EnumType.STRING)
@Column(length = 16)
var topic: Topic,

@Column(length = 256)
var title: String,

@Column(length = 2048)
var content: String,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
var writer: User,

@Column(length = 2048)
var imageUrl: String? = null,
): BaseEntity()
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.daitssuapi.domain.main.model.entity

import com.example.daitssuapi.common.audit.BaseEntity
import jakarta.persistence.*

@Entity
@Table(schema = "main")
class Reaction(
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "article_id")
val article: Article,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
val user: User,
): BaseEntity()
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.daitssuapi.domain.main.model.entity

import com.example.daitssuapi.common.audit.BaseEntity
import com.example.daitssuapi.domain.main.model.entity.Department
import jakarta.persistence.*

@Entity
@Table(schema = "main", name = "users")
class User(
val studentId: Int,

val name: String,

val nickname: String? = null,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
val department: Department,

val imageUrl: String? = null
) : BaseEntity()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.daitssuapi.domain.main.model.repository

import com.example.daitssuapi.domain.main.model.entity.Article
import org.springframework.data.jpa.repository.JpaRepository

interface ArticleRepository: JpaRepository<Article, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.daitssuapi.domain.main.model.repository

import com.example.daitssuapi.domain.main.model.entity.Reaction
import org.springframework.data.jpa.repository.JpaRepository

interface ReactionRepository: JpaRepository<Reaction, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.daitssuapi.domain.main.model.repository

import com.example.daitssuapi.domain.main.model.entity.User
import org.springframework.data.jpa.repository.JpaRepository

interface UserRepository : JpaRepository<User, Long> {
fun findByStudentId(studentId: Int): User?
fun findByNickname(nickname: String?): User?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.example.daitssuapi.domain.main.service

import com.example.daitssuapi.common.enums.ErrorCode
import com.example.daitssuapi.common.exception.DefaultException
import com.example.daitssuapi.domain.main.dto.request.ArticleWriteRequest
import com.example.daitssuapi.domain.main.dto.response.ArticleResponse
import com.example.daitssuapi.domain.main.model.entity.Article
import com.example.daitssuapi.domain.main.model.repository.ArticleRepository
import com.example.daitssuapi.domain.main.model.entity.User
import com.example.daitssuapi.domain.main.model.repository.UserRepository
import jakarta.transaction.Transactional
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class ArticleService(
private val articleRepository: ArticleRepository,
private val userRepository: UserRepository
) {
fun getArticle(id: Long): ArticleResponse {
val article: Article = articleRepository.findByIdOrNull(id)
?: throw DefaultException(ErrorCode.ARTICLE_NOT_FOUND)

return ArticleResponse(
id = article.id,
topic = article.topic.value,
title = article.title,
content = article.content,
writerNickName = article.writer.nickname!!,
updatedAt = article.updatedAt
)
}

@Transactional
fun writeArticle(articleWriteRequest: ArticleWriteRequest): ArticleResponse {
if (articleWriteRequest.nickname == null) {
throw DefaultException(ErrorCode.NICKNAME_REQUIRED)
}

val user: User = userRepository.findByNickname(articleWriteRequest.nickname)
?: throw DefaultException(ErrorCode.USER_NOT_FOUND)

val article: Article = Article(
topic = articleWriteRequest.topic,
title = articleWriteRequest.title,
content = articleWriteRequest.content,
writer = user
)

val savedArticle = articleRepository.save(article)

return ArticleResponse(
id = savedArticle.id,
topic = savedArticle.topic.value,
title = savedArticle.title,
content = savedArticle.content,
writerNickName = user.nickname!!,
updatedAt = savedArticle.updatedAt
)
}
}
Loading

0 comments on commit d09ec5d

Please sign in to comment.