diff --git a/src/main/kotlin/com/psr/psr/chat/controller/ChatController.kt b/src/main/kotlin/com/psr/psr/chat/controller/ChatController.kt new file mode 100644 index 0000000..99bb40d --- /dev/null +++ b/src/main/kotlin/com/psr/psr/chat/controller/ChatController.kt @@ -0,0 +1,89 @@ +package com.psr.psr.chat.controller + +import com.psr.psr.chat.dto.request.ChatMessageReq +import com.psr.psr.chat.service.ChatService +import com.psr.psr.global.dto.BaseResponse +import com.psr.psr.global.jwt.UserAccount +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.responses.ApiResponses +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.validation.Valid +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("/chat") +@Tag(name = "Chat", description = "채팅 API") +@SecurityRequirement(name = "Bearer") +class ChatController( + private val chatService: ChatService +) { + + /** + * 채팅방 등록 + */ + @Operation(summary = "채팅방 등록(박소정)", description = "채팅방을 등록한다.") + @ApiResponses( + value = [ + ApiResponse(responseCode = "200", description = "요청에 성공했습니다."), + ApiResponse( + responseCode = "404", + description = "해당 요청을 찾을 수 없습니다.", + content = arrayOf(Content(schema = Schema(implementation = BaseResponse::class))) + )] + ) + @PostMapping("/rooms/{orderId}") + fun createChatRoom(@AuthenticationPrincipal userAccount: UserAccount, + @Parameter(description = "(Long) 요청 id", example = "1") @PathVariable orderId: Long + ): BaseResponse { + return BaseResponse(chatService.createChatRoom(userAccount.getUser(), orderId)) + } + + /** + * 채팅방 나가기 + */ + @Operation(summary = "채팅방 나가기(박소정)", description = "채팅방을 나간다.") + @ApiResponses( + value = [ + ApiResponse(responseCode = "200", description = "요청에 성공했습니다."), + ApiResponse( + responseCode = "400", + description = "해당 채팅방을 찾을 수 없습니다.", + content = arrayOf(Content(schema = Schema(implementation = BaseResponse::class))) + )] + ) + @PatchMapping("/rooms/{chatRoomId}") + fun leaveChatRoom(@AuthenticationPrincipal userAccount: UserAccount, + @Parameter(description = "(Long) 채팅방 id", example = "1") @PathVariable chatRoomId: Long + ): BaseResponse { + return BaseResponse(chatService.leaveChatRoom(userAccount.getUser(), chatRoomId)); + } + + /** + * 메시지 전송 + */ + @Operation(summary = "메시지 전송(박소정)", description = "채팅방에 메시지를 전송한다.") + @ApiResponses( + value = [ + ApiResponse(responseCode = "200", description = "요청에 성공했습니다."), + ApiResponse( + responseCode = "404", + description = "해당 요청을 찾을 수 없습니다.", + content = arrayOf(Content(schema = Schema(implementation = BaseResponse::class))) + )] + ) + @PostMapping("/{chatRoomId}") + fun createChatMessage(@AuthenticationPrincipal userAccount: UserAccount, + @Parameter(description = "(Long) 채팅방 id", example = "1") @PathVariable chatRoomId: Long, + @RequestBody @Valid request: ChatMessageReq + ): BaseResponse { + return BaseResponse(chatService.createChatMessage(userAccount.getUser(), chatRoomId, request)) + } + + +} \ No newline at end of file diff --git a/src/main/kotlin/com/psr/psr/chat/controller/ChatRoomController.kt b/src/main/kotlin/com/psr/psr/chat/controller/ChatRoomController.kt deleted file mode 100644 index cd244d8..0000000 --- a/src/main/kotlin/com/psr/psr/chat/controller/ChatRoomController.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.psr.psr.chat.controller - -import com.psr.psr.chat.service.ChatRoomService -import com.psr.psr.global.dto.BaseResponse -import com.psr.psr.global.jwt.UserAccount -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import io.swagger.v3.oas.annotations.media.Content -import io.swagger.v3.oas.annotations.media.Schema -import io.swagger.v3.oas.annotations.responses.ApiResponse -import io.swagger.v3.oas.annotations.responses.ApiResponses -import io.swagger.v3.oas.annotations.security.SecurityRequirement -import io.swagger.v3.oas.annotations.tags.Tag -import org.springframework.security.core.annotation.AuthenticationPrincipal -import org.springframework.web.bind.annotation.* - -@RestController -@RequestMapping("/chatRooms") -@Tag(name = "ChatRoom", description = "채팅방 API") -@SecurityRequirement(name = "Bearer") -class ChatRoomController( - private val chatRoomService: ChatRoomService -) { - - /** - * 채팅방 등록 - */ - @Operation(summary = "채팅방 등록(박소정)", description = "채팅방을 등록한다.") - @ApiResponses( - value = [ - ApiResponse(responseCode = "200", description = "요청에 성공했습니다."), - ApiResponse( - responseCode = "404", - description = "해당 요청을 찾을 수 없습니다.", - content = arrayOf(Content(schema = Schema(implementation = BaseResponse::class))) - )] - ) - @PostMapping("/{orderId}") - fun createChatRoom(@AuthenticationPrincipal userAccount: UserAccount, - @Parameter(description = "(Long) 요청 id", example = "1") @PathVariable orderId: Long - ): BaseResponse { - return BaseResponse(chatRoomService.createChatRoom(userAccount.getUser(), orderId)) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/psr/psr/chat/dto/request/ChatMessageReq.kt b/src/main/kotlin/com/psr/psr/chat/dto/request/ChatMessageReq.kt new file mode 100644 index 0000000..2e222dd --- /dev/null +++ b/src/main/kotlin/com/psr/psr/chat/dto/request/ChatMessageReq.kt @@ -0,0 +1,14 @@ +package com.psr.psr.chat.dto.request + + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank + +data class ChatMessageReq( + + @Schema(description = "메세지", example = "안녕하세요~") + @field:NotBlank(message = "메세지를 입력해주세요.") + val message: String + +) + diff --git a/src/main/kotlin/com/psr/psr/chat/entity/ChatMessage.kt b/src/main/kotlin/com/psr/psr/chat/entity/ChatMessage.kt index cb506f0..39749cd 100644 --- a/src/main/kotlin/com/psr/psr/chat/entity/ChatMessage.kt +++ b/src/main/kotlin/com/psr/psr/chat/entity/ChatMessage.kt @@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull @Entity data class ChatMessage( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - var id: Long, + var id: Long? = null, @ManyToOne @JoinColumn(nullable = false, name = "sender_user_id") @@ -21,4 +21,14 @@ data class ChatMessage( @NotNull var message: String -) : BaseEntity() +) : BaseEntity() { + companion object { + fun toEntity(user: User, chatRoom: ChatRoom, message: String): ChatMessage { + return ChatMessage( + senderUser = user, + chatRoom = chatRoom, + message = message + ) + } + } +} diff --git a/src/main/kotlin/com/psr/psr/chat/entity/ChatRoom.kt b/src/main/kotlin/com/psr/psr/chat/entity/ChatRoom.kt index 99eb9b9..2c6168f 100644 --- a/src/main/kotlin/com/psr/psr/chat/entity/ChatRoom.kt +++ b/src/main/kotlin/com/psr/psr/chat/entity/ChatRoom.kt @@ -11,18 +11,23 @@ data class ChatRoom( var id: Long? = null, @ManyToOne - @JoinColumn(nullable = false, name = "sender_user_id") - var senderUser: User, + @JoinColumn(nullable = true, name = "sender_user_id") + var senderUser: User?, @ManyToOne - @JoinColumn(nullable = false, name = "receiver_user_id") - var receiverUser: User, + @JoinColumn(nullable = true, name = "receiver_user_id") + var receiverUser: User?, @OneToOne - @JoinColumn(nullable = true, name = "order_id") - var order: Order + @JoinColumn(nullable = false, name = "order_id") + var order: Order? ) : BaseEntity() { + fun leave(user: User) { + if (senderUser == user) senderUser = null + if (receiverUser == user) receiverUser = null + } + companion object { fun toEntity(user: User, order: Order): ChatRoom { return ChatRoom( diff --git a/src/main/kotlin/com/psr/psr/chat/repository/ChatMessageRepository.kt b/src/main/kotlin/com/psr/psr/chat/repository/ChatMessageRepository.kt new file mode 100644 index 0000000..a7cb788 --- /dev/null +++ b/src/main/kotlin/com/psr/psr/chat/repository/ChatMessageRepository.kt @@ -0,0 +1,9 @@ +package com.psr.psr.chat.repository + +import com.psr.psr.chat.entity.ChatMessage +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface ChatMessageRepository : JpaRepository { +} \ No newline at end of file diff --git a/src/main/kotlin/com/psr/psr/chat/repository/ChatRoomRepository.kt b/src/main/kotlin/com/psr/psr/chat/repository/ChatRoomRepository.kt index 6d68b43..bc58ffc 100644 --- a/src/main/kotlin/com/psr/psr/chat/repository/ChatRoomRepository.kt +++ b/src/main/kotlin/com/psr/psr/chat/repository/ChatRoomRepository.kt @@ -1,6 +1,7 @@ package com.psr.psr.chat.repository import com.psr.psr.chat.entity.ChatRoom +import com.psr.psr.order.entity.Order import com.psr.psr.user.entity.User import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository @@ -9,4 +10,6 @@ import org.springframework.stereotype.Repository interface ChatRoomRepository: JpaRepository { fun deleteBySenderUser(user: User) fun deleteByReceiverUser(user: User) + fun findByIdAndStatus(chatRoomId: Long, activeStatus: String): ChatRoom? + } \ No newline at end of file diff --git a/src/main/kotlin/com/psr/psr/chat/service/ChatRoomService.kt b/src/main/kotlin/com/psr/psr/chat/service/ChatRoomService.kt deleted file mode 100644 index dd7d18f..0000000 --- a/src/main/kotlin/com/psr/psr/chat/service/ChatRoomService.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.psr.psr.chat.service - -import com.psr.psr.chat.entity.ChatRoom -import com.psr.psr.chat.repository.ChatRoomRepository -import com.psr.psr.global.Constant -import com.psr.psr.global.exception.BaseException -import com.psr.psr.global.exception.BaseResponseCode -import com.psr.psr.order.entity.Order -import com.psr.psr.order.repository.OrderRepository -import com.psr.psr.user.entity.User -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -@Service -class ChatRoomService( - private val orderRepository: OrderRepository, - private val chatRoomRepository: ChatRoomRepository -) { - @Transactional - fun createChatRoom(user: User, orderId: Long) { - val order: Order = orderRepository.findByIdAndStatus(orderId, Constant.UserStatus.ACTIVE_STATUS) - ?: throw BaseException(BaseResponseCode.NOT_FOUND_ORDER) - chatRoomRepository.save(ChatRoom.toEntity(user, order)); - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/psr/psr/chat/service/ChatService.kt b/src/main/kotlin/com/psr/psr/chat/service/ChatService.kt new file mode 100644 index 0000000..780681b --- /dev/null +++ b/src/main/kotlin/com/psr/psr/chat/service/ChatService.kt @@ -0,0 +1,49 @@ +package com.psr.psr.chat.service + +import com.psr.psr.chat.dto.request.ChatMessageReq +import com.psr.psr.chat.entity.ChatMessage +import com.psr.psr.chat.entity.ChatRoom +import com.psr.psr.chat.repository.ChatMessageRepository +import com.psr.psr.chat.repository.ChatRoomRepository +import com.psr.psr.global.Constant +import com.psr.psr.global.exception.BaseException +import com.psr.psr.global.exception.BaseResponseCode +import com.psr.psr.order.entity.Order +import com.psr.psr.order.repository.OrderRepository +import com.psr.psr.user.entity.User +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class ChatService( + private val orderRepository: OrderRepository, + private val chatRoomRepository: ChatRoomRepository, + private val chatMessageRepository: ChatMessageRepository +) { + @Transactional + fun createChatRoom(user: User, orderId: Long) { + val order: Order = orderRepository.findByIdAndStatus(orderId, Constant.UserStatus.ACTIVE_STATUS) + ?: throw BaseException(BaseResponseCode.NOT_FOUND_ORDER) + chatRoomRepository.save(ChatRoom.toEntity(user, order)) + } + + @Transactional + fun leaveChatRoom(user: User, chatRoomId: Long) { + val chatRoom: ChatRoom = chatRoomRepository.findByIdAndStatus(chatRoomId, Constant.UserStatus.ACTIVE_STATUS) + ?: throw BaseException(BaseResponseCode.NOT_FOUND_CHATROOM) + chatRoom.leave(user) + checkChatRoom(chatRoom) + } + + private fun checkChatRoom(chatRoom: ChatRoom) { + if(chatRoom.senderUser==null && chatRoom.receiverUser==null) + chatRoomRepository.delete(chatRoom) + } + + @Transactional + fun createChatMessage(user: User, chatRoomId: Long, request: ChatMessageReq) { + val chatRoom: ChatRoom = chatRoomRepository.findByIdAndStatus(chatRoomId, Constant.UserStatus.ACTIVE_STATUS) + ?: throw BaseException(BaseResponseCode.NOT_FOUND_CHATROOM) + chatMessageRepository.save(ChatMessage.toEntity(user, chatRoom, request.message)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/psr/psr/global/exception/BaseResponseCode.kt b/src/main/kotlin/com/psr/psr/global/exception/BaseResponseCode.kt index b31cdda..966653d 100644 --- a/src/main/kotlin/com/psr/psr/global/exception/BaseResponseCode.kt +++ b/src/main/kotlin/com/psr/psr/global/exception/BaseResponseCode.kt @@ -77,7 +77,10 @@ enum class BaseResponseCode(status: HttpStatus, message: String) { // product NOT_FOUND_PRODUCT(HttpStatus.NOT_FOUND, "해당 상품을 찾을 수 없습니다."), NULL_PRODUCT_ID(HttpStatus.BAD_REQUEST, "상품ID를 입력해주세요."), - INVALID_PRODUCT_USER(HttpStatus.BAD_REQUEST, "해당 글 작성자가 아닙니다."); + INVALID_PRODUCT_USER(HttpStatus.BAD_REQUEST, "해당 글 작성자가 아닙니다."), + + // chatRoom + NOT_FOUND_CHATROOM(HttpStatus.NOT_FOUND, "해당 채팅방을 찾을 수 없습니다."); val status: HttpStatus = status val message: String = message