Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 채팅방 목록 조회 API #201

Merged
merged 4 commits into from
Nov 27, 2023
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
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.psr.psr.chat.controller

import com.psr.psr.chat.service.ChatRoomService
import com.psr.psr.chat.dto.request.ChatMessageReq
import com.psr.psr.chat.dto.response.GetChatRoomsRes
import com.psr.psr.chat.service.ChatService
import com.psr.psr.global.dto.BaseResponse
import com.psr.psr.global.jwt.UserAccount
import com.psr.psr.product.dto.request.CreateproductReq
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.Content
Expand All @@ -17,11 +18,11 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/chatRooms")
@Tag(name = "ChatRoom", description = "채팅방 API")
@RequestMapping("/chat")
@Tag(name = "Chat", description = "채팅 API")
@SecurityRequirement(name = "Bearer")
class ChatRoomController(
private val chatRoomService: ChatRoomService
class ChatController(
private val chatService: ChatService
) {

/**
Expand All @@ -37,11 +38,11 @@ class ChatRoomController(
content = arrayOf(Content(schema = Schema(implementation = BaseResponse::class)))
)]
)
@PostMapping("/{orderId}")
@PostMapping("/rooms/{orderId}")
fun createChatRoom(@AuthenticationPrincipal userAccount: UserAccount,
@Parameter(description = "(Long) 요청 id", example = "1") @PathVariable orderId: Long
): BaseResponse<Unit> {
return BaseResponse(chatRoomService.createChatRoom(userAccount.getUser(), orderId))
return BaseResponse(chatService.createChatRoom(userAccount.getUser(), orderId))
}

/**
Expand All @@ -57,11 +58,49 @@ class ChatRoomController(
content = arrayOf(Content(schema = Schema(implementation = BaseResponse::class)))
)]
)
@PatchMapping("/{chatRoomId}")
@PatchMapping("/rooms/{chatRoomId}")
fun leaveChatRoom(@AuthenticationPrincipal userAccount: UserAccount,
@Parameter(description = "(Long) 채팅방 id", example = "1") @PathVariable chatRoomId: Long
): BaseResponse<Unit> {
return BaseResponse(chatRoomService.leaveChatRoom(userAccount.getUser(), chatRoomId));
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<Unit> {
return BaseResponse(chatService.createChatMessage(userAccount.getUser(), chatRoomId, request))
}


/**
* 채팅방 목록 조회
*/
@Operation(summary = "채팅방 목록 조회(박소정)", description = "채팅방 목록을 조회한다.")
@ApiResponses(
value = [
ApiResponse(responseCode = "200", description = "요청에 성공했습니다.")]
)
@GetMapping("/rooms")
fun getChatRooms(
@AuthenticationPrincipal userAccount: UserAccount
): BaseResponse<GetChatRoomsRes?> {
return BaseResponse(chatService.getChatRooms(userAccount.getUser()));
}


Expand Down
14 changes: 14 additions & 0 deletions src/main/kotlin/com/psr/psr/chat/dto/request/ChatMessageReq.kt
Original file line number Diff line number Diff line change
@@ -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

)

53 changes: 53 additions & 0 deletions src/main/kotlin/com/psr/psr/chat/dto/response/ChatRoomRes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.psr.psr.chat.dto.response

import com.fasterxml.jackson.annotation.JsonFormat
import com.psr.psr.chat.entity.ChatMessage
import com.psr.psr.chat.entity.ChatRoom
import com.psr.psr.user.entity.User
import com.querydsl.core.annotations.QueryProjection
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDateTime

data class ChatRoomRes @QueryProjection constructor(
@Schema(description = "채팅방 id", example = "1")
val chatRoomId: Long,

@Schema(description = "프로필 이미지", example = "imgUrl")
val profileImgUrl: String? = null,

@Schema(description = "닉네임", example = "마리")
val nickname: String,

@Schema(description = "최근 메시지", example = "안녕하세요!")
val message: String? = null,

@Schema(description = "최근 메시지 날짜", example = "11.30")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MM.dd")
val date: LocalDateTime? = null,

@Schema(description = "본인이 메시지 읽음 여부", example = "false")
val isRead: Boolean,

@Schema(description = "읽지 않은 메시지 수 (없으면 0)", example = "3")
val numOfUnread: Int? = null

) {
companion object {
fun toDto(chatRoom: ChatRoom, chatMessage: ChatMessage, partner: User, numOfUnread: Int): ChatRoomRes {
val isRead: Boolean =
if (chatMessage.senderUser == partner) chatMessage.isRead
else true

return ChatRoomRes(
chatRoom.id!!,
partner.imgUrl,
partner.nickname,
chatMessage.message,
chatMessage.createdAt,
isRead,
numOfUnread
)

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.psr.psr.chat.dto.response

import com.querydsl.core.annotations.QueryProjection
import io.swagger.v3.oas.annotations.media.Schema

data class GetChatRoomsRes @QueryProjection constructor(
@Schema(description = "채팅방 리스트")
val noticeLists: List<ChatRoomRes>?
) {
companion object {
fun toDto(response: MutableList<ChatRoomRes>): GetChatRoomsRes? {
return GetChatRoomsRes(response)
}
}
}
19 changes: 16 additions & 3 deletions src/main/kotlin/com/psr/psr/chat/entity/ChatMessage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -19,6 +19,19 @@ data class ChatMessage(
var chatRoom: ChatRoom,

@NotNull
var message: String
var message: String,

) : BaseEntity()
@NotNull
var isRead: Boolean = false,

) : BaseEntity() {
companion object {
fun toEntity(user: User, chatRoom: ChatRoom, message: String): ChatMessage {
return ChatMessage(
senderUser = user,
chatRoom = chatRoom,
message = message
)
}
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/com/psr/psr/chat/entity/ChatRoom.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ data class ChatRoom(

@OneToOne
@JoinColumn(nullable = false, name = "order_id")
var order: Order?
var order: Order

) : BaseEntity() {
fun leave(user: User) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.psr.psr.chat.repository

import com.psr.psr.chat.dto.response.GetChatRoomsRes
import com.psr.psr.chat.entity.ChatRoom
import com.psr.psr.user.entity.User


interface ChatMessageCustom {
fun getRecentChat(user: User, chatRooms: List<ChatRoom>): GetChatRoomsRes?

}
Original file line number Diff line number Diff line change
@@ -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<ChatMessage, Long>, ChatMessageCustom {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.psr.psr.chat.repository

import com.psr.psr.chat.dto.response.ChatRoomRes
import com.psr.psr.chat.dto.response.GetChatRoomsRes
import com.psr.psr.chat.entity.ChatMessage
import com.psr.psr.chat.entity.ChatRoom
import com.psr.psr.chat.entity.QChatMessage.chatMessage
import com.psr.psr.global.Constant.UserStatus.UserStatus.ACTIVE_STATUS
import com.psr.psr.user.entity.User
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.stereotype.Component

@Component
class ChatMessageRepositoryImpl(
private val queryFactory: JPAQueryFactory
) : ChatMessageCustom {
override fun getRecentChat(user: User, chatRooms: List<ChatRoom>): GetChatRoomsRes? {

val response: MutableList<ChatRoomRes> = mutableListOf()

for (chatRoom in chatRooms) {
val chat: ChatMessage = queryFactory
.selectFrom(chatMessage)
.where(
chatMessage.chatRoom.eq(chatRoom),
chatMessage.status.eq(ACTIVE_STATUS),
)
.orderBy(chatMessage.updatedAt.desc())
.fetchFirst()

val numOfUnread: Int = queryFactory
.select(chatMessage)
.from(chatMessage)
.where(
chatMessage.chatRoom.eq(chatRoom),
chatMessage.status.eq(ACTIVE_STATUS),
chatMessage.isRead.eq(false),
!chatMessage.senderUser.eq(user)
)
.fetch()
.size

val partner: User =
if (user == chatRoom.order.user) chatRoom.order.product.user
else chatRoom.order.user

val chatRoomRes: ChatRoomRes = ChatRoomRes.toDto(chatRoom, chat, partner, numOfUnread)
response.add(chatRoomRes)
}

return GetChatRoomsRes.toDto(response)
}

}
10 changes: 10 additions & 0 deletions src/main/kotlin/com/psr/psr/chat/repository/ChatRoomCustom.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.psr.psr.chat.repository

import com.psr.psr.chat.entity.ChatRoom
import com.psr.psr.user.entity.User


interface ChatRoomCustom {
fun getChatRooms(user: User): List<ChatRoom>?

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
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

@Repository
interface ChatRoomRepository: JpaRepository<ChatRoom, Long> {
interface ChatRoomRepository : JpaRepository<ChatRoom, Long>, ChatRoomCustom {
fun deleteBySenderUser(user: User)
fun deleteByReceiverUser(user: User)
fun findByIdAndStatus(chatRoomId: Long, activeStatus: String): ChatRoom?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.psr.psr.chat.repository

import com.psr.psr.chat.entity.ChatRoom
import com.psr.psr.chat.entity.QChatRoom.chatRoom
import com.psr.psr.global.Constant.UserStatus.UserStatus.ACTIVE_STATUS
import com.psr.psr.user.entity.User
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.stereotype.Component

@Component
class ChatRoomRepositoryImpl(
private val queryFactory: JPAQueryFactory
) : ChatRoomCustom {
override fun getChatRooms(user: User): List<ChatRoom> {
return queryFactory
.selectFrom(chatRoom)
.where(
chatRoom.status.eq(ACTIVE_STATUS),
(chatRoom.senderUser.eq(user)).or(chatRoom.receiverUser.eq(user))
)
.orderBy(chatRoom.updatedAt.desc())
.fetch()
}

}
Loading
Loading