Skip to content

Commit

Permalink
Merge pull request #4 from wafflestudio/feat/auth
Browse files Browse the repository at this point in the history
Feat/article
  • Loading branch information
alpakar02 authored Jan 6, 2025
2 parents 64cd263 + 01182aa commit f9b2361
Show file tree
Hide file tree
Showing 13 changed files with 441 additions and 78 deletions.
24 changes: 24 additions & 0 deletions src/main/kotlin/com/toyProject7/karrot/article/ArticleException.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.toyProject7.karrot.article

import com.toyProject7.karrot.DomainException
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatusCode

sealed class ArticleException(
errorCode: Int,
httpStatusCode: HttpStatusCode,
msg: String,
cause: Throwable? = null,
) : DomainException(errorCode, httpStatusCode, msg, cause)

class ArticleNotFoundException : ArticleException(
errorCode = 0,
httpStatusCode = HttpStatus.NOT_FOUND,
msg = "Article not found",
)

class ArticlePermissionDeniedException : ArticleException(
errorCode = 0,
httpStatusCode = HttpStatus.UNAUTHORIZED,
msg = "Permission denied",
)
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
package com.toyProject7.karrot.article.controller

class Article
import com.toyProject7.karrot.article.persistence.ArticleEntity
import com.toyProject7.karrot.user.controller.User
import java.time.Instant

data class Article(
val id: Long,
val seller: User,
val title: String,
val content: String,
val price: Int,
val status: String,
val location: String,
val createdAt: Instant,
val likeCount: Int,
) {
companion object {
fun fromEntity(entity: ArticleEntity): Article {
return Article(
id = entity.id!!,
seller = User.fromEntity(entity.seller),
title = entity.title,
content = entity.content,
price = entity.price,
status = entity.status,
location = entity.location,
createdAt = entity.createdAt,
likeCount = entity.articleLikes.size,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,100 @@
package com.toyProject7.karrot.article.controller

class ArticleController
import com.toyProject7.karrot.article.persistence.ArticleEntity
import com.toyProject7.karrot.article.service.ArticleService
import com.toyProject7.karrot.user.AuthUser
import com.toyProject7.karrot.user.controller.User
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
class ArticleController(
private val articleService: ArticleService,
) {
@PostMapping("api/item/post")
fun postArticle(
@RequestBody request: PostArticleRequest,
@AuthUser user: User,
): ResponseEntity<Article> {
val article = articleService.postArticle(request, user.id)
return ResponseEntity.ok(article)
}

@PutMapping("api/item/edit/{articleId}")
fun editArticle(
@RequestBody request: PostArticleRequest,
@PathVariable articleId: Long,
@AuthUser user: User,
): ResponseEntity<Article> {
val article = articleService.editArticle(articleId, request, user.id)
return ResponseEntity.ok(article)
}

@DeleteMapping("api/item/delete/{articleId}")
fun deleteArticle(
@PathVariable articleId: Long,
@AuthUser user: User,
): ResponseEntity<String> {
articleService.deleteArticle(articleId, user.id)
return ResponseEntity.ok("Deleted Successfully")
}

@GetMapping("api/item/get/{articleId}")
fun getArticle(
@PathVariable articleId: Long,
): ResponseEntity<Article> {
return ResponseEntity.ok(articleService.getArticle(articleId))
}

@GetMapping("/api/home")
fun getPreviousArticles(
@RequestParam("articleId") articleId: Long,
): ResponseEntity<List<Item>> {
val articles: List<ArticleEntity> = articleService.getPreviousArticles(articleId)
val response: List<Item> =
articles.map { article ->
Item.fromArticle(Article.fromEntity(article))
}
return ResponseEntity.ok(response)
}

@GetMapping("/api/mypage/sells")
fun getArticlesBySeller(
@RequestParam("articleId") articleId: Long,
@AuthUser user: User,
): ResponseEntity<List<Item>> {
val articles: List<ArticleEntity> = articleService.getArticlesBySeller(user.id, articleId)
val response: List<Item> =
articles.map { article ->
Item.fromArticle(Article.fromEntity(article))
}
return ResponseEntity.ok(response)
}

@GetMapping("/api/mypage/buys")
fun getArticlesByBuyer(
@RequestParam("articleId") articleId: Long,
@AuthUser user: User,
): ResponseEntity<List<Item>> {
val articles = articleService.getArticlesByBuyer(user.id, articleId)
val response: List<Item> =
articles.map { article ->
Item.fromArticle(Article.fromEntity(article))
}
return ResponseEntity.ok(response)
}
}

data class PostArticleRequest(
val title: String,
val content: String,
val price: Int,
val location: String,
)
28 changes: 28 additions & 0 deletions src/main/kotlin/com/toyProject7/karrot/article/controller/Item.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.toyProject7.karrot.article.controller

import java.time.Instant

// article's preview information
data class Item(
val id: Long,
val title: String,
val price: Int,
val status: String,
val location: String,
val createdAt: Instant,
val likeCount: Int,
) {
companion object {
fun fromArticle(article: Article): Item {
return Item(
id = article.id,
title = article.title,
price = article.price,
status = article.status,
location = article.location,
createdAt = article.createdAt,
likeCount = article.likeCount,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
package com.toyProject7.karrot.article.persistence

class ArticleEntity
import com.toyProject7.karrot.user.persistence.UserEntity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.OneToMany
import java.time.Instant

@Entity(name = "article")
class ArticleEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@ManyToOne
@JoinColumn(name = "seller")
var seller: UserEntity,
@ManyToOne
@JoinColumn(name = "buyer")
var buyer: UserEntity?,
@Column(name = "title", nullable = false)
var title: String,
@Column(name = "content", nullable = false)
var content: String,
@Column(name = "price", nullable = false)
var price: Int,
@Column(name = "status", nullable = false)
var status: String,
@Column(name = "location", nullable = false)
var location: String,
@OneToMany(mappedBy = "article")
var articleLikes: MutableList<ArticleLikesEntity> = mutableListOf(),
@Column(name = "created_at", nullable = false)
var createdAt: Instant,
@Column(name = "updated_at", nullable = false)
var updatedAt: Instant,
)
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
package com.toyProject7.karrot.article.persistence

class ArticleLikesEntity
import com.toyProject7.karrot.user.persistence.UserEntity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import java.time.Instant

@Entity(name = "article_likes")
class ArticleLikesEntity(
@Id
@GeneratedValue(strategy = GenerationType.UUID)
val id: String? = null,
@ManyToOne
@JoinColumn(name = "article_id")
var article: ArticleEntity,
@ManyToOne
@JoinColumn(name = "user")
var user: UserEntity,
@Column(name = "created_at", nullable = false)
var createdAt: Instant,
@Column(name = "updated_at", nullable = false)
var updatedAt: Instant,
)
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package com.toyProject7.karrot.article.persistence

class ArticleLikesRepository
import org.springframework.data.jpa.repository.JpaRepository

interface ArticleLikesRepository : JpaRepository<ArticleLikesEntity, String>
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
package com.toyProject7.karrot.article.persistence

class ArticleRepository
import com.toyProject7.karrot.user.persistence.UserEntity
import org.springframework.data.jpa.repository.JpaRepository

interface ArticleRepository : JpaRepository<ArticleEntity, Long> {
fun findTop10ByIdBeforeOrderByCreatedAtDesc(id: Long): List<ArticleEntity>

fun findTop10BySellerAndIdLessThanOrderByIdDesc(
seller: UserEntity,
id: Long,
): List<ArticleEntity>

fun findTop10ByBuyerAndIdLessThanOrderByIdDesc(
buyer: UserEntity,
id: Long,
): List<ArticleEntity>
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,104 @@
package com.toyProject7.karrot.article.service

class ArticleService
import com.toyProject7.karrot.article.ArticleNotFoundException
import com.toyProject7.karrot.article.ArticlePermissionDeniedException
import com.toyProject7.karrot.article.controller.Article
import com.toyProject7.karrot.article.controller.PostArticleRequest
import com.toyProject7.karrot.article.persistence.ArticleEntity
import com.toyProject7.karrot.article.persistence.ArticleRepository
import com.toyProject7.karrot.user.service.UserService
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.Instant

@Service
class ArticleService(
private val articleRepository: ArticleRepository,
private val userService: UserService,
) {
@Transactional
fun postArticle(
request: PostArticleRequest,
id: String,
): Article {
val user = userService.getUserEntityById(id)
val articleEntity =
ArticleEntity(
seller = user,
buyer = null,
title = request.title,
content = request.content,
price = request.price,
status = "판매 중",
location = request.location,
createdAt = Instant.now(),
updatedAt = Instant.now(),
).let {
articleRepository.save(it)
}
return Article.fromEntity(articleEntity)
}

@Transactional
fun editArticle(
articleId: Long,
request: PostArticleRequest,
id: String,
): Article {
val user = userService.getUserEntityById(id)
val articleEntity = articleRepository.findByIdOrNull(articleId) ?: throw ArticleNotFoundException()
if (articleEntity.seller.userId != user.userId) {
throw ArticlePermissionDeniedException()
}
request.title.let { articleEntity.title = it }
request.content.let { articleEntity.content = it }
request.price.let { articleEntity.price = it }
request.location.let { articleEntity.location = it }
articleEntity.updatedAt = Instant.now()
articleRepository.save(articleEntity)
return Article.fromEntity(articleEntity)
}

@Transactional
fun deleteArticle(
articleId: Long,
id: String,
) {
val user = userService.getUserEntityById(id)
val articleEntity = articleRepository.findByIdOrNull(articleId) ?: throw ArticleNotFoundException()
if (articleEntity.seller.userId != user.userId) {
throw ArticlePermissionDeniedException()
}
articleRepository.delete(articleEntity)
}

@Transactional
fun getArticle(articleId: Long): Article {
val articleEntity = articleRepository.findByIdOrNull(articleId) ?: throw ArticleNotFoundException()
return Article.fromEntity(articleEntity)
}

@Transactional
fun getPreviousArticles(articleId: Long): List<ArticleEntity> {
return articleRepository.findTop10ByIdBeforeOrderByCreatedAtDesc(articleId)
}

@Transactional
fun getArticlesBySeller(
id: String,
articleId: Long,
): List<ArticleEntity> {
val seller = userService.getUserEntityById(id)
return articleRepository.findTop10BySellerAndIdLessThanOrderByIdDesc(seller, articleId)
}

@Transactional
fun getArticlesByBuyer(
id: String,
articleId: Long,
): List<ArticleEntity> {
val buyer = userService.getUserEntityById(id)
return articleRepository.findTop10ByBuyerAndIdLessThanOrderByIdDesc(buyer, articleId)
}
}
Loading

0 comments on commit f9b2361

Please sign in to comment.