Skip to content

Commit 194bf57

Browse files
committed
feat: adfadf
1 parent 4472f35 commit 194bf57

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+927
-69
lines changed

api/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies {
3636
implementation("org.redisson:redisson-spring-boot-starter:3.27.2")
3737

3838
testImplementation(kotlin("test"))
39+
testImplementation("redis.clients:jedis:5.1.3")
3940
testImplementation("com.redis.testcontainers:testcontainers-redis-junit:1.6.4")
4041
testImplementation("org.testcontainers:mysql:1.16.0")
4142
testImplementation("org.testcontainers:testcontainers:1.19.0")

api/src/main/kotlin/com/backgu/amaker/api/AMakerApiApplication.kt

+8-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ import org.springframework.scheduling.annotation.EnableScheduling
1313
@EntityScan(basePackages = ["com.backgu.amaker.infra"])
1414
@EnableRedisRepositories(basePackages = ["com.backgu.amaker.infra.redis"])
1515
@EnableJpaRepositories(basePackages = ["com.backgu.amaker.infra"])
16-
@SpringBootApplication(scanBasePackages = ["com.backgu.amaker.api", "com.backgu.amaker.infra.jpa"])
16+
@SpringBootApplication(
17+
scanBasePackages = [
18+
"com.backgu.amaker.api",
19+
"com.backgu.amaker.infra.jpa",
20+
"com.backgu.amaker.infra.redis.chat",
21+
"com.backgu.amaker.infra.redis.user",
22+
],
23+
)
1724
class AMakerApiApplication
1825

1926
fun main(args: Array<String>) {
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,53 @@
11
package com.backgu.amaker.api.chat.config
22

3+
import com.backgu.amaker.application.chat.service.ChatCacheService
4+
import com.backgu.amaker.application.chat.service.ChatQueryService
35
import com.backgu.amaker.application.chat.service.ChatRoomService
6+
import com.backgu.amaker.application.chat.service.ChatRoomUserCacheService
47
import com.backgu.amaker.application.chat.service.ChatRoomUserService
58
import com.backgu.amaker.application.chat.service.ChatService
9+
import com.backgu.amaker.application.chat.service.ChatUserCacheService
10+
import com.backgu.amaker.application.user.service.UserCacheService
11+
import com.backgu.amaker.application.user.service.UserService
612
import com.backgu.amaker.infra.jpa.chat.repository.ChatRepository
713
import com.backgu.amaker.infra.jpa.chat.repository.ChatRoomRepository
814
import com.backgu.amaker.infra.jpa.chat.repository.ChatRoomUserRepository
15+
import com.backgu.amaker.infra.redis.chat.repository.ChatCacheRepository
16+
import com.backgu.amaker.infra.redis.chat.repository.ChatRoomUserCacheRepository
917
import org.springframework.context.annotation.Bean
1018
import org.springframework.context.annotation.Configuration
1119

1220
@Configuration
13-
class ChatServiceConfig(
14-
val chatRepository: ChatRepository,
15-
val chatRoomUserRepository: ChatRoomUserRepository,
16-
val chatRoomRepository: ChatRoomRepository,
17-
) {
21+
class ChatServiceConfig {
1822
@Bean
19-
fun chatService() = ChatService(chatRepository)
23+
fun chatService(chatRepository: ChatRepository) = ChatService(chatRepository)
2024

2125
@Bean
22-
fun chatRoomUserService() = ChatRoomUserService(chatRoomUserRepository)
26+
fun chatRoomUserService(chatRoomUserRepository: ChatRoomUserRepository) = ChatRoomUserService(chatRoomUserRepository)
2327

2428
@Bean
25-
fun chatRoomService() = ChatRoomService(chatRoomRepository)
29+
fun chatRoomService(chatRoomRepository: ChatRoomRepository) = ChatRoomService(chatRoomRepository)
30+
31+
@Bean
32+
fun chatQueryService(chatRepository: ChatRepository) = ChatQueryService(chatRepository)
33+
34+
@Bean
35+
fun chatCacheService(chatCacheRepository: ChatCacheRepository) = ChatCacheService(chatCacheRepository)
36+
37+
@Bean
38+
fun chatRoomUserCacheService(chatRoomUserCacheRepository: ChatRoomUserCacheRepository) =
39+
ChatRoomUserCacheService(chatRoomUserCacheRepository)
40+
41+
@Bean
42+
fun chatUserCacheService(
43+
chatCacheService: ChatCacheService,
44+
userCacheService: UserCacheService,
45+
chatRoomUserCacheService: ChatRoomUserCacheService,
46+
userService: UserService,
47+
) = ChatUserCacheService(
48+
chatCacheService,
49+
userCacheService,
50+
chatRoomUserCacheService,
51+
userService,
52+
)
2653
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,46 @@
11
package com.backgu.amaker.api.chat.dto
22

3+
import com.backgu.amaker.api.event.dto.EventWithUserDto
34
import com.backgu.amaker.api.user.dto.UserDto
45
import com.backgu.amaker.domain.chat.ChatType
6+
import com.backgu.amaker.domain.chat.ChatWithUser
7+
import com.backgu.amaker.domain.chat.DefaultChatWithUser
8+
import com.backgu.amaker.domain.event.EventWithUser
59
import java.time.LocalDateTime
610

7-
interface ChatWithUserDto<T> {
11+
sealed interface ChatWithUserDto<T> {
812
val id: Long
913
val chatRoomId: Long
1014
val content: T
1115
val chatType: ChatType
1216
val createdAt: LocalDateTime
1317
val updatedAt: LocalDateTime
1418
val user: UserDto
19+
20+
companion object {
21+
fun <T> of(chatWithUser: ChatWithUser<T>): ChatWithUserDto<out Any> =
22+
when (chatWithUser) {
23+
is DefaultChatWithUser ->
24+
DefaultChatWithUserDto(
25+
id = chatWithUser.id,
26+
chatRoomId = chatWithUser.chatRoomId,
27+
content = chatWithUser.content,
28+
chatType = chatWithUser.chatType,
29+
createdAt = chatWithUser.createdAt,
30+
updatedAt = chatWithUser.updatedAt,
31+
user = UserDto.of(chatWithUser.user),
32+
)
33+
34+
else ->
35+
EventChatWithUserDto(
36+
id = chatWithUser.id,
37+
chatRoomId = chatWithUser.chatRoomId,
38+
content = EventWithUserDto.of(chatWithUser.content as EventWithUser),
39+
chatType = chatWithUser.chatType,
40+
createdAt = chatWithUser.createdAt,
41+
updatedAt = chatWithUser.updatedAt,
42+
user = UserDto.of(chatWithUser.user),
43+
)
44+
}
45+
}
1546
}

api/src/main/kotlin/com/backgu/amaker/api/chat/dto/DefaultChatWithUserDto.kt

+26-12
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package com.backgu.amaker.api.chat.dto
33
import com.backgu.amaker.api.user.dto.UserDto
44
import com.backgu.amaker.domain.chat.Chat
55
import com.backgu.amaker.domain.chat.ChatType
6+
import com.backgu.amaker.domain.chat.DefaultChatWithUser
67
import com.backgu.amaker.domain.user.User
7-
import com.backgu.amaker.infra.jpa.chat.query.ChatWithUserQuery
88
import java.time.LocalDateTime
99

1010
class DefaultChatWithUserDto(
@@ -31,21 +31,35 @@ class DefaultChatWithUserDto(
3131
user = UserDto(id = user.id, name = user.name, email = user.email, picture = user.picture),
3232
)
3333

34-
fun of(chatWithUserQuery: ChatWithUserQuery) =
34+
fun of(chatWithUser: DefaultChatWithUser) =
3535
DefaultChatWithUserDto(
36-
id = chatWithUserQuery.id,
37-
chatRoomId = chatWithUserQuery.chatRoomId,
38-
content = chatWithUserQuery.content,
39-
chatType = chatWithUserQuery.chatType,
40-
createdAt = chatWithUserQuery.createdAt,
41-
updatedAt = chatWithUserQuery.updatedAt,
36+
id = chatWithUser.id,
37+
chatRoomId = chatWithUser.chatRoomId,
38+
content = chatWithUser.content,
39+
chatType = chatWithUser.chatType,
40+
createdAt = chatWithUser.createdAt,
41+
updatedAt = chatWithUser.updatedAt,
4242
user =
4343
UserDto(
44-
id = chatWithUserQuery.user.id,
45-
name = chatWithUserQuery.user.name,
46-
email = chatWithUserQuery.user.email,
47-
picture = chatWithUserQuery.user.picture,
44+
id = chatWithUser.user.id,
45+
name = chatWithUser.user.name,
46+
email = chatWithUser.user.email,
47+
picture = chatWithUser.user.picture,
4848
),
4949
)
50+
51+
fun of(
52+
defaultChatWithUser: DefaultChatWithUser,
53+
user: User,
54+
): DefaultChatWithUserDto =
55+
DefaultChatWithUserDto(
56+
id = defaultChatWithUser.id,
57+
chatRoomId = defaultChatWithUser.chatRoomId,
58+
content = defaultChatWithUser.content,
59+
chatType = defaultChatWithUser.chatType,
60+
createdAt = defaultChatWithUser.createdAt,
61+
updatedAt = defaultChatWithUser.updatedAt,
62+
user = UserDto(id = user.id, name = user.name, email = user.email, picture = user.picture),
63+
)
5064
}
5165
}

api/src/main/kotlin/com/backgu/amaker/api/chat/dto/EventChatWithUserDto.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.backgu.amaker.api.chat.dto
33
import com.backgu.amaker.api.event.dto.EventWithUserDto
44
import com.backgu.amaker.api.user.dto.UserDto
55
import com.backgu.amaker.domain.chat.ChatType
6+
import com.backgu.amaker.domain.chat.ChatWithUser
67
import java.time.LocalDateTime
78

89
class EventChatWithUserDto(
@@ -16,7 +17,7 @@ class EventChatWithUserDto(
1617
) : ChatWithUserDto<EventWithUserDto> {
1718
companion object {
1819
fun of(
19-
chat: ChatWithUserDto<*>,
20+
chat: ChatWithUser<*>,
2021
eventDto: EventWithUserDto,
2122
): EventChatWithUserDto =
2223
EventChatWithUserDto(
@@ -26,7 +27,7 @@ class EventChatWithUserDto(
2627
chatType = chat.chatType,
2728
createdAt = chat.createdAt,
2829
updatedAt = chat.updatedAt,
29-
user = chat.user,
30+
user = UserDto.of(chat.user),
3031
)
3132
}
3233
}

api/src/main/kotlin/com/backgu/amaker/api/chat/service/ChatFacadeService.kt

+53-8
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import com.backgu.amaker.api.chat.dto.ChatQuery
66
import com.backgu.amaker.api.chat.dto.ChatWithUserDto
77
import com.backgu.amaker.api.chat.dto.DefaultChatWithUserDto
88
import com.backgu.amaker.api.chat.dto.EventChatWithUserDto
9-
import com.backgu.amaker.api.chat.service.query.ChatQueryService
109
import com.backgu.amaker.api.event.dto.EventWithUserDto
10+
import com.backgu.amaker.application.chat.service.ChatQueryService
1111
import com.backgu.amaker.application.chat.service.ChatRoomService
1212
import com.backgu.amaker.application.chat.service.ChatRoomUserService
1313
import com.backgu.amaker.application.chat.service.ChatService
14+
import com.backgu.amaker.application.chat.service.ChatUserCacheService
1415
import com.backgu.amaker.application.event.service.EventAssignedUserService
1516
import com.backgu.amaker.application.event.service.EventService
1617
import com.backgu.amaker.application.user.service.UserService
@@ -20,6 +21,7 @@ import com.backgu.amaker.domain.chat.Chat
2021
import com.backgu.amaker.domain.chat.ChatRoom
2122
import com.backgu.amaker.domain.chat.ChatRoomUser
2223
import com.backgu.amaker.domain.chat.ChatType
24+
import com.backgu.amaker.domain.chat.DefaultChatWithUser
2325
import com.backgu.amaker.domain.event.Event
2426
import com.backgu.amaker.domain.event.EventAssignedUser
2527
import com.backgu.amaker.domain.user.User
@@ -36,6 +38,7 @@ class ChatFacadeService(
3638
private val eventService: EventService,
3739
private val chatQueryService: ChatQueryService,
3840
private val eventAssignedUserService: EventAssignedUserService,
41+
private val chatUserCacheService: ChatUserCacheService,
3942
) {
4043
@Transactional
4144
fun createChat(
@@ -50,6 +53,8 @@ class ChatFacadeService(
5053
val chat: Chat = chatService.save(chatRoom.createChat(user, chatCreateDto.content, chatType))
5154
chatRoomService.save(chatRoom.updateLastChatId(chat))
5255

56+
chatUserCacheService.saveDefaultChat(chatRoomId, chat.createDefaultChatWithUser(user))
57+
5358
return DefaultChatWithUserDto.of(chat, user)
5459
}
5560

@@ -59,16 +64,37 @@ class ChatFacadeService(
5964
chatQuery: ChatQuery,
6065
): ChatListDto {
6166
markMostRecentChatAsRead(chatQuery.chatRoomId, userId)
62-
val chatList = chatQueryService.findPreviousChatList(chatQuery.chatRoomId, chatQuery.cursor, chatQuery.size)
67+
68+
val cachedChats =
69+
chatUserCacheService.findPreviousChats(chatQuery.chatRoomId, chatQuery.cursor, chatQuery.size)
70+
val dbQuerySize = chatQuery.size - cachedChats.size
71+
72+
if (dbQuerySize <= 0) {
73+
return ChatListDto.of(
74+
chatQuery,
75+
cachedChats.map { ChatWithUserDto.of(it) },
76+
)
77+
}
78+
79+
val firstChatId = cachedChats.firstOrNull()?.id ?: chatQuery.cursor
80+
val chatListFromDB =
81+
chatQueryService.findPreviousChatList(chatQuery.chatRoomId, firstChatId, dbQuerySize)
6382

6483
val eventMap =
65-
eventService.findEventByIdsToMap(chatList.filter { ChatType.isEventChat(it.chatType) }.map { it.id })
84+
eventService.findEventByIdsToMap(chatListFromDB.filter { ChatType.isEventChat(it.chatType) }.map { it.id })
6685
val eventUserMap = eventAssignedUserService.findByEventIdsToEventIdMapped(eventMap.keys.toList())
6786
val userMap = userService.findAllByUserIdsToMap(eventUserMap.values.flatten().map { it.userId })
6887

6988
return ChatListDto.of(
7089
chatQuery,
71-
chatList.map { mapToChatWithUser(it, eventMap, eventUserMap, userMap) },
90+
chatListFromDB.map {
91+
mapToChatWithUser(
92+
it,
93+
eventMap,
94+
eventUserMap,
95+
userMap,
96+
)
97+
} + cachedChats.map { ChatWithUserDto.of(it) },
7298
)
7399
}
74100

@@ -78,7 +104,17 @@ class ChatFacadeService(
78104
chatQuery: ChatQuery,
79105
): ChatListDto {
80106
markMostRecentChatAsRead(chatQuery.chatRoomId, userId)
107+
108+
println("===================")
109+
chatUserCacheService
110+
.findAfterChats(chatQuery.chatRoomId, chatQuery.cursor, chatQuery.size)
111+
?.let { return ChatListDto.of(chatQuery, it.map { chatWithUser -> ChatWithUserDto.of(chatWithUser) }) }
112+
113+
println("===================")
81114
val chatList = chatQueryService.findAfterChatList(chatQuery.chatRoomId, chatQuery.cursor, chatQuery.size)
115+
for (defaultChatWithUser in chatList) {
116+
println(defaultChatWithUser.id)
117+
}
82118

83119
val eventMap =
84120
eventService.findEventByIdsToMap(chatList.filter { ChatType.isEventChat(it.chatType) }.map { it.id })
@@ -97,9 +133,17 @@ class ChatFacadeService(
97133
chatRoomId: Long,
98134
): ChatWithUserDto<*> {
99135
val chatRoomUser = markMostRecentChatAsRead(chatRoomId, userId)
136+
137+
val cachedChat =
138+
chatRoomUser.lastReadChatId?.let {
139+
chatUserCacheService.findChat(chatRoomId, it)
140+
}
141+
142+
if (cachedChat != null) return ChatWithUserDto.of(cachedChat)
143+
100144
val chat = chatQueryService.getOneWithUser(chatRoomUser.lastReadChatId)
101145

102-
if (!ChatType.isEventChat(chat.chatType)) return chat
146+
if (!ChatType.isEventChat(chat.chatType)) return DefaultChatWithUserDto.of(chat)
103147

104148
val event = eventService.getEventById(chat.id)
105149
val eventAssignedUsers = eventAssignedUserService.findAllByEventId(event.id)
@@ -112,7 +156,8 @@ class ChatFacadeService(
112156
)
113157
}
114158

115-
private fun markMostRecentChatAsRead(
159+
@Transactional
160+
fun markMostRecentChatAsRead(
116161
chatRoomId: Long,
117162
userId: String,
118163
): ChatRoomUser {
@@ -123,7 +168,7 @@ class ChatFacadeService(
123168
}
124169

125170
private fun mapToChatWithUser(
126-
chat: DefaultChatWithUserDto,
171+
chat: DefaultChatWithUser,
127172
eventMap: Map<Long, Event>,
128173
eventUserMap: Map<Long, List<EventAssignedUser>>,
129174
userMap: Map<String, User>,
@@ -134,6 +179,6 @@ class ChatFacadeService(
134179
val finishedNumber = eventUserMap[event.id]?.count { it.isFinished } ?: 0
135180
EventChatWithUserDto.of(chat, EventWithUserDto.of(event, eventUsers, finishedNumber))
136181
} else {
137-
chat
182+
DefaultChatWithUserDto.of(chat)
138183
}
139184
}

api/src/main/kotlin/com/backgu/amaker/api/chat/service/ChatRoomFacadeService.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class ChatRoomFacadeService(
5757

5858
// 채팅방에 참여한 유저들의 도메인 조회
5959
val userMap: Map<String, User> =
60-
userService.findAllByUserIds(chatRoomUsers.map { it.userId }).associateBy { it.id }
60+
userService.getAllByUserIds(chatRoomUsers.map { it.userId }).associateBy { it.id }
6161

6262
// 채팅방의 읽지 않은 채팅 수 조회
6363
val unreadChatCountMap =

api/src/main/kotlin/com/backgu/amaker/api/config/RedisConfig.kt

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import org.springframework.context.annotation.Configuration
66
import org.springframework.data.redis.connection.RedisConnectionFactory
77
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
88
import org.springframework.data.redis.core.RedisTemplate
9-
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
109
import org.springframework.data.redis.serializer.StringRedisSerializer
1110

1211
@Configuration
@@ -25,11 +24,11 @@ class RedisConfig {
2524
}
2625

2726
@Bean
28-
fun redisTemplate(): RedisTemplate<String, Long> {
29-
val template = RedisTemplate<String, Long>()
27+
fun redisTemplate(): RedisTemplate<String, Any> {
28+
val template = RedisTemplate<String, Any>()
3029
template.connectionFactory = redisConnectionFactory()
3130
template.keySerializer = StringRedisSerializer()
32-
template.valueSerializer = GenericJackson2JsonRedisSerializer()
31+
template.valueSerializer = StringRedisSerializer()
3332
return template
3433
}
3534
}

0 commit comments

Comments
 (0)