Skip to content

Commit

Permalink
Merge pull request #114 from soma-baekgu/feature/BG-257-ongoing-event…
Browse files Browse the repository at this point in the history
…-retrieve

[BG-257]:  워크스페이스 이벤트 조회(3H/3H)
  • Loading branch information
Dltmd202 authored Oct 23, 2024
2 parents c1dfa6a + eb1131f commit d3fcb52
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ class ChatFacadeService(
userMap: Map<String, User>,
): ChatWithUserDto<*> =
if (ChatType.isEventChat(chat.chatType)) {
val event = eventMap[chat.id] ?: throw BusinessException(StatusCode.EVENT_NOT_FOUND)
val eventUsers = eventUserMap[event.id]?.mapNotNull { userMap[it.userId] } ?: emptyList()
val finishedNumber = eventUserMap[event.id]?.count { it.isFinished } ?: 0
val event: Event = eventMap[chat.id] ?: throw BusinessException(StatusCode.EVENT_NOT_FOUND)
val eventUsers: List<User> = eventUserMap[event.id]?.mapNotNull { userMap[it.userId] } ?: emptyList()
val finishedNumber: Int = eventUserMap[event.id]?.count { it.isFinished } ?: 0
EventChatWithUserDto.of(chat, EventWithUserDto.of(event, eventUsers, finishedNumber))
} else {
DefaultChatWithUserDto.of(chat)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.backgu.amaker.api.event.controller

import com.backgu.amaker.api.event.dto.response.EventBriefResponse
import com.backgu.amaker.api.event.service.EventFacadeService
import com.backgu.amaker.common.http.ApiHandler
import com.backgu.amaker.common.http.response.ApiResult
import com.backgu.amaker.common.security.jwt.authentication.JwtAuthentication
import com.backgu.amaker.domain.event.EventStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.util.Locale

@RestController
@RequestMapping("/api/v1/workspaces/{workspace-id}/events")
class EventQueryController(
private val eventFacadeService: EventFacadeService,
private val apiHandler: ApiHandler,
) : EventQuerySwagger {
@GetMapping
override fun getOngoingEvent(
@AuthenticationPrincipal token: JwtAuthentication,
@PathVariable("workspace-id") workspaceId: Long,
@RequestParam status: String,
): ResponseEntity<ApiResult<List<EventBriefResponse>>> {
val eventStatus = EventStatus.valueOf(status.uppercase(Locale.getDefault()))
return ResponseEntity.ok().body(
apiHandler.onSuccess(
eventFacadeService
.getEvents(
token.id,
workspaceId,
eventStatus,
).map { EventBriefResponse.of(it, token.id) },
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.backgu.amaker.api.event.controller

import com.backgu.amaker.api.event.dto.response.EventBriefResponse
import com.backgu.amaker.common.http.response.ApiResult
import com.backgu.amaker.common.security.jwt.authentication.JwtAuthentication
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.PathVariable

@Tag(name = "event-query", description = "이벤트 조회 API")
interface EventQuerySwagger {
@Operation(summary = "진행중인 이벤트 조회", description = "진행중인 이벤트 리스트 조회입니다.")
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "진행중인 이벤트 리스트 조회 성공",
),
],
)
fun getOngoingEvent(
@AuthenticationPrincipal token: JwtAuthentication,
@PathVariable("workspace-id") workspaceId: Long,
status: String,
): ResponseEntity<ApiResult<List<EventBriefResponse>>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.backgu.amaker.api.event.dto

import com.backgu.amaker.api.user.dto.UserDto
import com.backgu.amaker.domain.chat.Chat
import com.backgu.amaker.domain.event.Event
import com.backgu.amaker.domain.user.User
import java.time.LocalDateTime

data class EventWithUserAndChatRoomDto(
val id: Long,
val chatRoomId: Long,
val eventTitle: String,
val deadLine: LocalDateTime,
val notificationStartTime: LocalDateTime,
val notificationInterval: Int,
val users: List<UserDto>,
val finishedCount: Int,
val totalAssignedCount: Int,
) {
companion object {
fun of(
event: Event,
chat: Chat,
users: List<User>,
finishedCount: Int = 0,
): EventWithUserAndChatRoomDto =
EventWithUserAndChatRoomDto(
id = event.id,
chatRoomId = chat.chatRoomId,
eventTitle = event.eventTitle,
deadLine = event.deadLine,
notificationStartTime = event.notificationStartTime,
notificationInterval = event.notificationInterval,
users = users.map { UserDto.of(it) },
finishedCount = finishedCount,
totalAssignedCount = users.size,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.backgu.amaker.api.event.dto.response

import com.backgu.amaker.api.event.dto.EventWithUserAndChatRoomDto
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDateTime

data class EventBriefResponse(
@Schema(
description = "이벤트 아이디",
example = "2",
)
val eventId: Long,
@Schema(
description = "채팅방 아이디",
example = "21",
)
val chatRoomId: Long,
@Schema(
description = "이벤트 제목",
example = "우리 어디서 만날지",
)
val eventTitle: String,
@Schema(
description = "데드라인",
example = "2024-07-24T07:39:37.598",
)
val deadLine: LocalDateTime,
@Schema(
description = "알림 보낼 시작 시간",
example = "2024-07-24T06:09:37.598",
)
val notificationStartTime: LocalDateTime,
@Schema(
description = "알림 주기",
example = "15",
)
val notificationInterval: Int,
@Schema(
description = "유저 사진 리스트",
example = "[\"http://a-maker.com/hi1.png\", \"http://a-maker.com/hi2.png\"]",
)
val users: List<String>,
@Schema(
description = "완료된 이벤트 수",
example = "2",
)
val finishedCount: Int,
@Schema(
description = "총 배정된 이벤트 수",
example = "5",
)
val totalAssignedCount: Int,
@Schema(
description = "이벤트가 자신의 것인지",
example = "true",
)
val isMine: Boolean,
) {
companion object {
fun of(
event: EventWithUserAndChatRoomDto,
userId: String,
) = EventBriefResponse(
eventId = event.id,
chatRoomId = event.chatRoomId,
eventTitle = event.eventTitle,
deadLine = event.deadLine,
notificationStartTime = event.notificationStartTime,
notificationInterval = event.notificationInterval,
users = event.users.map { it.picture },
finishedCount = event.finishedCount,
totalAssignedCount = event.totalAssignedCount,
isMine = event.users.any { it.id == userId },
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.backgu.amaker.api.event.service

import com.backgu.amaker.api.event.dto.EventWithUserAndChatRoomDto
import com.backgu.amaker.api.event.dto.ReactionEventCreateDto
import com.backgu.amaker.api.event.dto.ReactionEventDetailDto
import com.backgu.amaker.api.event.dto.ReactionEventDto
Expand All @@ -12,14 +13,20 @@ import com.backgu.amaker.application.chat.service.ChatRoomService
import com.backgu.amaker.application.chat.service.ChatRoomUserService
import com.backgu.amaker.application.chat.service.ChatService
import com.backgu.amaker.application.event.service.EventAssignedUserService
import com.backgu.amaker.application.event.service.EventService
import com.backgu.amaker.application.event.service.ReactionEventService
import com.backgu.amaker.application.event.service.ReactionOptionService
import com.backgu.amaker.application.event.service.ReplyEventService
import com.backgu.amaker.application.user.service.UserService
import com.backgu.amaker.application.workspace.WorkspaceUserService
import com.backgu.amaker.common.exception.BusinessException
import com.backgu.amaker.common.status.StatusCode
import com.backgu.amaker.domain.chat.Chat
import com.backgu.amaker.domain.chat.ChatType
import com.backgu.amaker.domain.event.Event
import com.backgu.amaker.domain.event.EventAssignedUser
import com.backgu.amaker.domain.event.EventStatus
import com.backgu.amaker.domain.user.User
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand All @@ -36,6 +43,8 @@ class EventFacadeService(
private val reactionOptionService: ReactionOptionService,
private val eventAssignedUserService: EventAssignedUserService,
private val eventPublisher: ApplicationEventPublisher,
private val eventService: EventService,
private val workspaceUserService: WorkspaceUserService,
) {
@Transactional
fun getReplyEvent(
Expand Down Expand Up @@ -196,4 +205,29 @@ class EventFacadeService(

return ReactionEventDto.of(reactionEvent, reactionOptions)
}

fun getEvents(
userId: String,
workspaceId: Long,
eventStatus: EventStatus,
): List<EventWithUserAndChatRoomDto> {
workspaceUserService.validateUserInWorkspace(userId, workspaceId)
val events: List<Event> = eventService.findEventByWorkspaceId(workspaceId)
val eventUserMap: Map<Long, List<EventAssignedUser>> =
eventAssignedUserService.findByEventIdsToEventIdMapped(events.map { it.id })
val filteredEvents =
events.filter { eventStatus.filter(it, eventUserMap[it.id] ?: emptyList()) }
val chatToMap = chatService.findAllByIdsToMap(filteredEvents.map { it.id })

val userMap = userService.findAllByUserIdsToMap(eventUserMap.values.flatten().map { it.userId })

return filteredEvents.map { event: Event ->
val eventAssignedUsers: List<User> =
eventUserMap[event.id]?.mapNotNull { userMap[it.userId] } ?: emptyList()
val finishedNumber = eventUserMap[event.id]?.count { it.isFinished } ?: 0
val chat = chatToMap[event.id] ?: throw BusinessException(StatusCode.CHAT_NOT_FOUND)

EventWithUserAndChatRoomDto.of(event, chat, eventAssignedUsers, finishedNumber)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,8 @@ abstract class Event(
finishedCount,
totalAssignedCount,
)

fun isAchieved(user: List<EventAssignedUser>): Boolean = user.size == user.count { it.isFinished }

fun isClosed(): Boolean = LocalDateTime.now().isAfter(deadLine)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.backgu.amaker.domain.event

enum class EventStatus {
ONGOING {
override fun filter(
event: Event,
assignedUsers: List<EventAssignedUser>,
): Boolean = !event.isAchieved(assignedUsers) && !event.isClosed()
},
EXPIRED {
override fun filter(
event: Event,
assignedUsers: List<EventAssignedUser>,
): Boolean = event.isClosed() && !event.isAchieved(assignedUsers)
},
COMPLETED {
override fun filter(
event: Event,
assignedUsers: List<EventAssignedUser>,
): Boolean = event.isAchieved(assignedUsers)
}, ;

abstract fun filter(
event: Event,
assignedUsers: List<EventAssignedUser>,
): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.test.Test
class EventAssignedUserTest {
@Test
@DisplayName("isFinished 업데이트 테스트")
fun updateIsFinished() {
fun updateIsAchieved() {
// given
val eventAssignedUser =
EventAssignedUser(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ class ChatService(

fun findAllByIds(chatIds: List<Long>): List<Chat> = chatRepository.findAllById(chatIds).map { it.toDomain() }

fun findAllByIdsToMap(chatIds: List<Long>): Map<Long, Chat> =
chatRepository.findAllById(chatIds).map { it.toDomain() }.associateBy { it.id }

fun getById(chatId: Long) = chatRepository.findByIdOrNull(chatId)?.toDomain() ?: throw BusinessException(StatusCode.CHAT_NOT_FOUND)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ class EventService(

fun findEventByIdsToMap(eventIds: List<Long>): Map<Long, Event> =
eventRepository.findAllById(eventIds).map { it.toDomain() }.associateBy { it.id }

fun findEventByWorkspaceId(workspaceId: Long): List<Event> = eventRepository.findAllByWorkspaceId(workspaceId).map { it.toDomain() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,14 @@ package com.backgu.amaker.infra.jpa.event.repository

import com.backgu.amaker.infra.jpa.event.entity.EventEntity
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query

interface EventRepository : JpaRepository<EventEntity, Long>
interface EventRepository : JpaRepository<EventEntity, Long> {
@Query(
"SELECT e FROM Event e " +
"inner join Chat c on e.id = c.id " +
"inner join ChatRoom ch on ch.id = c.chatRoomId " +
"WHERE ch.workspaceId = :workspaceId",
)
fun findAllByWorkspaceId(workspaceId: Long): List<EventEntity>
}

0 comments on commit d3fcb52

Please sign in to comment.