diff --git a/src/main/kotlin/busanVibe/busan/domain/chat/controller/ChatRestController.kt b/src/main/kotlin/busanVibe/busan/domain/chat/controller/ChatRestController.kt index 9aa9e50..71e8cea 100644 --- a/src/main/kotlin/busanVibe/busan/domain/chat/controller/ChatRestController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/chat/controller/ChatRestController.kt @@ -28,11 +28,12 @@ class ChatRestController( 채팅 전송 API 입니다. 해당 API 호출 시 메시지가 전송되고, 채팅 웹소켓에 연결된 유저들에게 메시지가 전송됩니다. 메시지의 길이는 최대 200글자입니다. - type : ['CHAT', 'BOT'] + type : ['CHAT', 'BOT_RESPONSE', 'BOT_REQUEST'] 메시지가 '/' 로 시작하면 챗봇 답변을 응답합니다. 일반 채팅은 웹소켓으로 메시지를 전송하고, 챗봇은 웹소켓 메시지를 전송하지 않습니다. 따라서 일반 채팅은 웹소켓으로 받은 메시지로 활용하고, 챗봇에게 받은 답변은 해당 API의 응답 결과를 활용해주세요. + """) fun sendMessage(@Valid @RequestBody chatMessage: ChatMessageSendDTO): ApiResponse { val receiveDTO = chatMongoService.saveAndPublish(chatMessage) diff --git a/src/main/kotlin/busanVibe/busan/domain/chat/enums/MessageType.kt b/src/main/kotlin/busanVibe/busan/domain/chat/enums/MessageType.kt index 32d9b72..9dbd83a 100644 --- a/src/main/kotlin/busanVibe/busan/domain/chat/enums/MessageType.kt +++ b/src/main/kotlin/busanVibe/busan/domain/chat/enums/MessageType.kt @@ -1,5 +1,5 @@ package busanVibe.busan.domain.chat.enums enum class MessageType { - CHAT, BOT + CHAT, BOT_REQUEST, BOT_RESPONSE } \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/domain/chat/service/ChatMongoService.kt b/src/main/kotlin/busanVibe/busan/domain/chat/service/ChatMongoService.kt index dcfe257..72acde2 100644 --- a/src/main/kotlin/busanVibe/busan/domain/chat/service/ChatMongoService.kt +++ b/src/main/kotlin/busanVibe/busan/domain/chat/service/ChatMongoService.kt @@ -13,6 +13,7 @@ import busanVibe.busan.global.apiPayload.code.status.ErrorStatus import busanVibe.busan.global.apiPayload.exception.GeneralException import org.slf4j.Logger import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value import org.springframework.data.domain.Pageable import org.springframework.data.redis.listener.ChannelTopic import org.springframework.stereotype.Service @@ -24,7 +25,9 @@ class ChatMongoService( private val redisPublisher: RedisPublisher, private val topic: ChannelTopic, private val userRepository: UserRepository, - private val openAiService: OpenAiService + private val openAiService: OpenAiService, + @Value("\${image.chat-bot}") + private val chatBotImage: String ) { val log: Logger = LoggerFactory.getLogger(ChatMongoService::class.java) @@ -46,9 +49,9 @@ class ChatMongoService( val message = chatMessage.message.trim() // 받은 메세지로 타입 구분 - // '/'로 시작하면 챗봇(BOT) + // 일반 메시지는 CHAT, 챗봇 요청은 BOT_REQUEST val type: MessageType = if(message[0] == '/'){ - MessageType.BOT + MessageType.BOT_REQUEST }else{ MessageType.CHAT } @@ -64,16 +67,32 @@ class ChatMongoService( time = now ) - // 일반 채팅일 경우 채팅 기록 저장 + // 사용자가 보낸 메시지 저장 chatMongoRepository.save(chat) - // 유저들에게 웹소켓으로 전달할 메시지의 DTO 생성 + // 유저들에게 웹소켓으로 전달할 메시지의 DTO 생성 - CHAT val receiveDto = buildReceiveDto(type, currentUser, chat, now) // 일반 채팅일 경우에만 유저들에게 웹소켓 메시지 보냄 - if(type == MessageType.CHAT) { + if(receiveDto.type == MessageType.CHAT) { redisPublisher.publish(topic, receiveDto) } + + // OpenAI 챗봇 요청 + if (receiveDto.type == MessageType.BOT_RESPONSE) { + // openai 요청 + openAiService.chatToOpenAI(chat.message) + // 챗봇 대답 저장 + chatMongoRepository.save( + ChatMessage( + type = MessageType.BOT_RESPONSE, + userId = currentUser.id, + message = receiveDto.message, + time = chat.time + ) + ) + } + return receiveDto } @@ -97,11 +116,11 @@ class ChatMongoService( val message = openAiService.chatToOpenAI(chat.message) ChatMessageReceiveDTO( name = "챗봇", - imageUrl = null, + imageUrl = chatBotImage, message = message, time = timestamp, - type = type, - userId = -1 + type = MessageType.BOT_RESPONSE, + userId = currentUser.id ) } } diff --git a/src/main/kotlin/busanVibe/busan/domain/common/BaseEntity.kt b/src/main/kotlin/busanVibe/busan/domain/common/BaseEntity.kt index 241ce9e..af34f43 100644 --- a/src/main/kotlin/busanVibe/busan/domain/common/BaseEntity.kt +++ b/src/main/kotlin/busanVibe/busan/domain/common/BaseEntity.kt @@ -7,9 +7,7 @@ import lombok.Getter import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener -import java.time.Instant import java.time.LocalDateTime -import java.time.ZoneId @EntityListeners(AuditingEntityListener::class) @MappedSuperclass diff --git a/src/main/kotlin/busanVibe/busan/domain/user/service/UserCommandService.kt b/src/main/kotlin/busanVibe/busan/domain/user/service/UserCommandService.kt index 515653e..79f92f1 100644 --- a/src/main/kotlin/busanVibe/busan/domain/user/service/UserCommandService.kt +++ b/src/main/kotlin/busanVibe/busan/domain/user/service/UserCommandService.kt @@ -12,7 +12,6 @@ import busanVibe.busan.global.apiPayload.code.status.ErrorStatus import busanVibe.busan.global.apiPayload.exception.GeneralException import busanVibe.busan.global.apiPayload.exception.handler.ExceptionHandler import busanVibe.busan.global.config.security.JwtTokenProvider -import io.lettuce.core.KillArgs.Builder.user import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Qualifier import org.springframework.beans.factory.annotation.Value @@ -35,7 +34,10 @@ class UserCommandService( private val userRepository: UserRepository, private val userConverter: UserConverter, private val jwtTokenProvider: JwtTokenProvider, - private val passwordEncoder: PasswordEncoder + private val passwordEncoder: PasswordEncoder, + @Value("\${image.guest}") + private val guestImage: String + ) { @Value("\${spring.kakao.client-id}") @@ -49,8 +51,8 @@ class UserCommandService( fun guestLogin(): UserLoginResponseDTO.LoginDto { val email = UUID.randomUUID().toString().substring(0,7) + "@busanvibe.com" - val nickname = UUID.randomUUID().toString().substring(0, 7) - val profileImageUrl: String? = null + val nickname = "guest" + UUID.randomUUID().toString().substring(0, 4) + val profileImageUrl: String? = guestImage return isNewUser(email, nickname, profileImageUrl, LoginType.GUEST) } diff --git a/src/main/kotlin/busanVibe/busan/global/config/mongo/DateToLocalDateTimeKstConverter.kt b/src/main/kotlin/busanVibe/busan/global/config/mongo/DateToLocalDateTimeKstConverter.kt new file mode 100644 index 0000000..3b3214d --- /dev/null +++ b/src/main/kotlin/busanVibe/busan/global/config/mongo/DateToLocalDateTimeKstConverter.kt @@ -0,0 +1,16 @@ +package busanVibe.busan.global.config.mongo + +import org.springframework.core.convert.converter.Converter +import org.springframework.data.convert.ReadingConverter +import org.springframework.stereotype.Component +import java.time.LocalDateTime +import java.time.ZoneId +import java.util.Date + +@Component +@ReadingConverter +class DateToLocalDateTimeKstConverter: Converter { + + override fun convert(source: Date): LocalDateTime = + source.toInstant().atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime() +} \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/global/config/mongo/LocalDateTimeToDateKstConverter.kt b/src/main/kotlin/busanVibe/busan/global/config/mongo/LocalDateTimeToDateKstConverter.kt new file mode 100644 index 0000000..ffee46a --- /dev/null +++ b/src/main/kotlin/busanVibe/busan/global/config/mongo/LocalDateTimeToDateKstConverter.kt @@ -0,0 +1,18 @@ +package busanVibe.busan.global.config.mongo + +import org.springframework.core.convert.converter.Converter +import org.springframework.data.convert.WritingConverter +import org.springframework.stereotype.Component +import java.sql.Timestamp +import java.time.LocalDateTime +import java.time.ZoneId +import java.util.Date + +@Component +@WritingConverter +class LocalDateTimeToDateKstConverter: Converter { + + override fun convert(source: LocalDateTime): Date = + Date.from(source.atZone(ZoneId.of("Asia/Seoul")).toInstant()) + +} \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/global/config/mongo/MongoConfig.kt b/src/main/kotlin/busanVibe/busan/global/config/mongo/MongoConfig.kt index cb4de4e..2939e11 100644 --- a/src/main/kotlin/busanVibe/busan/global/config/mongo/MongoConfig.kt +++ b/src/main/kotlin/busanVibe/busan/global/config/mongo/MongoConfig.kt @@ -6,6 +6,7 @@ import org.springframework.boot.autoconfigure.mongo.MongoProperties import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.data.mongodb.core.MongoTemplate +import org.springframework.data.mongodb.core.convert.MongoCustomConversions import org.springframework.data.mongodb.repository.config.EnableMongoRepositories @Configuration @@ -24,4 +25,15 @@ class MongoConfig( return MongoTemplate(mongoClient(), mongoProperties.database) } + @Bean + fun customConversions( + localDateTimeConverter: LocalDateTimeToDateKstConverter, + dateToLocalDateTimeConverter: DateToLocalDateTimeKstConverter, + ) = MongoCustomConversions ( + listOf( + localDateTimeConverter, + dateToLocalDateTimeConverter + ) + ) + } \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/global/config/mongo/MongoTimeConfig.kt b/src/main/kotlin/busanVibe/busan/global/config/mongo/MongoTimeConfig.kt new file mode 100644 index 0000000..9a14b40 --- /dev/null +++ b/src/main/kotlin/busanVibe/busan/global/config/mongo/MongoTimeConfig.kt @@ -0,0 +1,28 @@ +package busanVibe.busan.global.config.mongo + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.core.convert.converter.Converter +import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration +import org.springframework.data.mongodb.core.convert.MongoCustomConversions +import java.time.LocalDateTime +import java.time.ZoneId +import java.util.Date + +//@Configuration +//class MongoTimeConfig: AbstractMongoClientConfiguration() { +// +// @Bean +// override fun customConversions(): MongoCustomConversions { +// return MongoCustomConversions( +// listOf( +// Converter { Date.from(it.atZone(ZoneId.of("Asia/Seoul")).toInstant()) }, +// Converter { it.toInstant().atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime() } +// ) +// ) +// } +// +// override fun getDatabaseName(): String { +// TODO("Not yet implemented") +// } +//} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7274e5a..a4d7f1f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -64,5 +64,6 @@ openai: tourAPI: key: ${TOUR_API_KEY} - - +image: + chat-bot: ${IMAGE_CHATBOT} + guest: ${IMAGE_GUEST} \ No newline at end of file