Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/src/main/kotlin/com/wespot/config/EnableRetryConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.wespot.config

import org.springframework.context.annotation.Configuration
import org.springframework.retry.annotation.EnableRetry

@EnableRetry
@Configuration
class EnableRetryConfig {

}
10 changes: 10 additions & 0 deletions app/src/main/kotlin/com/wespot/user/UserController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import com.wespot.user.dto.response.UserSettingResponse
import com.wespot.user.port.`in`.CheckedUserRestrictionUseCase
import com.wespot.user.port.`in`.UserSettingUseCase
import com.wespot.user.port.`in`.UserUseCase
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
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.RequestMapping
Expand All @@ -33,6 +35,14 @@ class UserController(
.body(response)
}

@PostMapping("/allow/policy")
fun allowNewPolicy(policyType: PolicyType): ResponseEntity<Unit> {
userUseCase.allowNewPolicy(policyType)

return ResponseEntity.status(HttpStatus.CREATED)
.build()
}

@PutMapping("/me")
fun updateProfile(
@RequestBody profile: UpdateProfileRequest
Expand Down
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ subprojects {

dependencies {

// retry
implementation("org.springframework.retry:spring-retry")
implementation("org.springframework.boot:spring-boot-starter-aop")

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-api:2.3.0")
implementation("io.swagger.core.v3:swagger-core:2.2.19")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.wespot.exception

class GetLockFailException(message: String) : RuntimeException(message) {

}
9 changes: 9 additions & 0 deletions common/src/main/kotlin/com/wespot/lock/ExecutorWithLock.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.wespot.lock

interface ExecutorWithLock {

fun execute(task: Runnable, lockKey: LockKey)

fun <RETURN> execute(task: () -> RETURN, lockKey: LockKey): RETURN

}
25 changes: 25 additions & 0 deletions common/src/main/kotlin/com/wespot/lock/LockKey.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.wespot.lock

import com.wespot.exception.CustomException
import org.springframework.http.HttpStatus


@JvmRecord
data class LockKey(val keywords: List<String>) {

init {
if (keywords.isEmpty()) {
throw CustomException(
message = "Lock Key는 최소 1개 이상의 Keyword를 가져야 합니다.",
status = HttpStatus.INTERNAL_SERVER_ERROR
)
}
}

constructor(vararg keywords: Any) : this(keywords.map { it.toString() }.toList())

val key: String
get() {
return keywords.joinToString(":") { it }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ object SecurityUtils {

fun getLoginUser(userPort: UserPort): User {
val principal = SecurityContextHolder.getContext().authentication.principal as PrincipalDetails

return userPort.findByEmail(principal.username)
?: throw CustomException(HttpStatus.NOT_FOUND, ExceptionView.TOAST, "해당 계정이 존재하지 않습니다.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import com.wespot.comment.port.`in`.PostCommentCreatedUseCase
import com.wespot.comment.port.out.PostCommentPort
import com.wespot.comment.port.out.PostValidatePort
import com.wespot.exception.CustomException
import com.wespot.lock.ExecutorWithLock
import com.wespot.lock.LockKey
import com.wespot.user.port.out.UserPort
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
Expand All @@ -19,25 +21,36 @@ class PostCommentCreatedService(
private val userPort: UserPort,
private val postCommentPort: PostCommentPort,
private val postValidatePort: PostValidatePort,
private val executorWithLock: ExecutorWithLock,
) : PostCommentCreatedUseCase {

@Transactional
override fun createComment(postCommentCreatedRequest: PostCommentCreatedRequest): Long {
if (!postValidatePort.existsPostById(postCommentCreatedRequest.postId)) {
throw CustomException(status = HttpStatus.BAD_REQUEST, message = "해당하는 게시글을 찾을 수 없습니다.")
}

val loginUser = SecurityUtils.getLoginUser(userPort = userPort)
val postComment = PostComment.of(
postId = postCommentCreatedRequest.postId,
content = postCommentCreatedRequest.content,
user = loginUser
)
return executorWithLock.execute(
task = {
if (!postValidatePort.existsPostById(postCommentCreatedRequest.postId)) {
throw CustomException(status = HttpStatus.BAD_REQUEST, message = "해당하는 게시글을 찾을 수 없습니다.")
}

val loginUser = SecurityUtils.getLoginUser(userPort = userPort)

if (loginUser.canNotUseCommunity()) {
throw CustomException(status = HttpStatus.FORBIDDEN, message = "커뮤니티 이용이 제한된 사용자입니다.")
}

val savedPostComment = postCommentPort.save(postComment)
EventUtils.publish(PostCommentCreatedEvent(savedPostComment))
val postComment = PostComment.of(
postId = postCommentCreatedRequest.postId,
content = postCommentCreatedRequest.content,
user = loginUser
)

return savedPostComment.id
val savedPostComment = postCommentPort.save(postComment)
EventUtils.publish(PostCommentCreatedEvent(savedPostComment))

savedPostComment.id
},
lockKey = LockKey("postComment", "${postCommentCreatedRequest.postId}"),
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import com.wespot.auth.service.SecurityUtils
import com.wespot.comment.event.PostCommentDeleteEvent
import com.wespot.comment.port.`in`.PostCommentDeletedUseCase
import com.wespot.comment.port.out.PostCommentPort
import com.wespot.exception.CustomException
import com.wespot.user.port.out.UserPort
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -18,6 +20,11 @@ class PostCommentDeletedService(
@Transactional
override fun deleteComment(commentId: Long) {
val loginUser = SecurityUtils.getLoginUser(userPort = userPort)

if (loginUser.canNotUseCommunity()) {
throw CustomException(status = HttpStatus.FORBIDDEN, message = "커뮤니티 이용이 제한된 사용자입니다.")
}

val postComment = postCommentPort.findById(id = commentId) ?: throw IllegalArgumentException("존재하지 않는 댓글입니다.")

if (!postComment.isAuthor(loginUser.id)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.wespot.comment.port.out.PostCommentLikePort
import com.wespot.comment.port.out.PostCommentPort
import com.wespot.exception.CustomException
import com.wespot.user.port.out.UserPort
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -20,6 +21,11 @@ class PostCommentLikeService(
@Transactional
override fun likeComment(commentId: Long) {
val loginUser = SecurityUtils.getLoginUser(userPort = userPort)

if (loginUser.canNotUseCommunity()) {
throw CustomException(status = HttpStatus.FORBIDDEN, message = "커뮤니티 이용이 제한된 사용자입니다.")
}

val postComment = postCommentPort.findById(commentId) ?: throw CustomException(message = "존재하지 않는 댓글입니다.")
postCommentLikePort.findByPostCommentIdAndUserId(postCommentId = commentId, userId = loginUser.id)
?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import com.wespot.auth.service.SecurityUtils
import com.wespot.comment.PostCommentReport
import com.wespot.comment.PostCommentReportReason
import com.wespot.comment.dto.PostCommentReportRequest
import com.wespot.comment.event.PostCommentReportEvent
import com.wespot.comment.port.`in`.PostCommentReportUseCase
import com.wespot.comment.port.out.PostCommentPort
import com.wespot.comment.port.out.PostCommentReportPort
import com.wespot.exception.CustomException
import com.wespot.report.ReportReasonWithCustomReason
import com.wespot.report.port.out.ReportReasonPort
import com.wespot.user.port.out.UserPort
import org.springframework.context.ApplicationEventPublisher
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -19,14 +22,24 @@ class PostCommentReportService(
private val userPort: UserPort,
private val postCommentReportPort: PostCommentReportPort,
private val postCommentPort: PostCommentPort,
private val reportReasonPort: ReportReasonPort
private val reportReasonPort: ReportReasonPort,
private val applicationEventPublisher: ApplicationEventPublisher,
) : PostCommentReportUseCase {

@Transactional
override fun reportComment(commentId: Long, request: PostCommentReportRequest?) {
val loginUser = SecurityUtils.getLoginUser(userPort = userPort)

if (loginUser.canNotUseCommunity()) {
throw CustomException(status = HttpStatus.FORBIDDEN, message = "커뮤니티 이용이 제한된 사용자입니다.")
}

val postComment = postCommentPort.findById(commentId) ?: throw CustomException(message = "존재하지 않는 댓글입니다.")

if (postComment.isAuthor(id = loginUser.id)) {
throw CustomException(message = "자신의 댓글은 신고할 수 없습니다.")
}

postCommentReportPort.findByPostCommentIdAndUserId(postCommentId = commentId, userId = loginUser.id)
?.let {
postCommentReportPort.deleteById(it.id)
Expand Down Expand Up @@ -56,6 +69,17 @@ class PostCommentReportService(

val addedReportPostComment = postComment.addReport()
postCommentPort.save(addedReportPostComment)

val reportReason = postCommentReportReasons.map { it.reportReasonWithCustomReason.reason() }
.joinToString(separator = ", ") { it }
applicationEventPublisher.publishEvent(
PostCommentReportEvent(
postComment = postComment,
sender = loginUser,
receiver = postComment.user,
reason = reportReason,
)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class HotPostComponentResponse(
}

data class HotPostResponse(
val targetId: Long = 0L,
val headerSection: HotPostHeaderSectionResponse,
val infoSection: HotPostInfoSectionResponse,
val createdAt: RichTextV2Response,
Expand Down Expand Up @@ -63,6 +64,7 @@ class HotPostComponentResponse(
),
posts = posts.map { post ->
HotPostContentResponse.HotPostResponse(
targetId = post.targetId,
headerSection = HotPostContentResponse.HotPostHeaderSectionResponse(
profileImage = ImageContentV2Response.from(post.headerSection.profileImage),
nickname = RichTextV2Response.from(post.headerSection.nickname)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.wespot.message.service.listener

import org.springframework.stereotype.Component

@Component
class MessageBlockedEventListener {
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.wespot.message.service.listener

import com.wespot.message.event.MessageV2BlockedEvent
import com.wespot.message.port.`in`.CreateMessageUseCase
import com.wespot.message.port.`in`.CreatedMessageV2UseCase
import com.wespot.user.block.BlockedUser
import com.wespot.user.event.WelcomeMessageEvent
import com.wespot.user.port.out.BlockedUserPort
import org.springframework.context.event.EventListener
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component
Expand All @@ -14,7 +17,8 @@ import org.springframework.transaction.event.TransactionalEventListener
@Component
class MessageEventListener(
private val createMessageUseCase: CreateMessageUseCase,
private val createdMessageV2UseCase: CreatedMessageV2UseCase
private val createdMessageV2UseCase: CreatedMessageV2UseCase,
private val blockedUserPort: BlockedUserPort,
) {

@EventListener
Expand All @@ -29,5 +33,38 @@ class MessageEventListener(
createdMessageV2UseCase.welcomeMessage(event.signUpUser)
}

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
@Transactional(propagation = Propagation.REQUIRES_NEW)
fun handleMessageBlockedEvent(event: MessageV2BlockedEvent) {
val message = event.message
if (message.isAnonymousRoom()) {
return
}

val sender = event.sender

if (message.isBlockedBy(viewer = sender)) {
val blockedUser = BlockedUser.create(
blockerId = sender.id,
blockedId = event.receiver.id,
messageId = message.id,
isAlreadyBlocked = blockedUserPort.existsByBlockerIdAndBlockedIdAndMessageId(
blockerId = sender.id,
blockedId = event.receiver.id,
messageId = message.id
)
)
blockedUserPort.save(blockedUser)
return
}

blockedUserPort.deleteByBlockerIdAndBlockedIdAndMessageId(
blockerId = sender.id,
blockedId = event.receiver.id,
messageId = message.id
)
}


}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.wespot.message.service.v2

import com.wespot.auth.service.SecurityUtils
import com.wespot.exception.CustomException
import com.wespot.message.dto.request.AnswerMessageRequest
import com.wespot.message.port.`in`.AnswerMessageV2UseCase
import com.wespot.message.port.out.MessageV2Port
import com.wespot.message.v2.MessageRoom
import com.wespot.user.port.out.UserPort
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -19,6 +21,10 @@ class AnswerMessageV2Service(
override fun answerMessage(messageRoomId: Long, answerMessageRequest: AnswerMessageRequest) {
val loginUser = SecurityUtils.getLoginUser(userPort = userPort)

if (loginUser.canNotUseMessage()) {
throw CustomException(message = "메시지 기능을 사용할 수 없는 사용자입니다.", status = HttpStatus.FORBIDDEN)
}

val message = messageV2Port.findById(id = messageRoomId)
val messageDetails = messageV2Port.findAllByMessageRoomId(message.id)
val alreadyUsedMessageOnToday = messageV2Port.countTodaySendMessages(senderId = loginUser.id)
Expand Down
Loading
Loading