Skip to content
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
Expand Up @@ -5,7 +5,6 @@ import com.didit.application.achievement.dto.MissionInfo
import com.didit.application.achievement.dto.PopupStatus
import com.didit.application.achievement.dto.WeeklyStatus
import com.didit.application.achievement.exception.CurrentMissionNotFoundException
import com.didit.application.achievement.exception.MissionDefinitionNotFoundException
import com.didit.application.achievement.exception.UserLevelNotFoundException
import com.didit.application.achievement.provided.MissionFinder
import com.didit.application.achievement.required.MissionRepository
Expand Down Expand Up @@ -34,11 +33,21 @@ class MissionQueryService(
) : MissionFinder {
override fun getCurrentMission(userId: UUID): CurrentMissionResponse {
val userLevel = userLevelRepository.findByUserId(userId) ?: throw UserLevelNotFoundException(userId)
val missionLevel = userLevel.currentLevel + 1
val mission = missionRepository.findByLevel(missionLevel)

if (mission == null) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate no-mission responses to max level

This branch treats every missing Mission definition as the terminal-level state. Because levels 1–10 are the configured progression and currentLevel + 1 can be any of those levels, a missing seed/deploy row for e.g. Lv3 now returns 200 with mission: null and weekly status for a Lv2 user instead of surfacing MissionDefinitionNotFoundException, making users look maxed out and hiding the data/configuration error. Please only take this path when the user is actually at the cap (or compare against the max mission level) and keep throwing for lower levels.

Useful? React with 👍 / 👎.

val lastMission = userMissionRepository.findByUserId(userId).firstOrNull()
return CurrentMissionResponse(
currentLevel = userLevel.currentLevel,
mission = null,
weeklyStatus = buildWeeklyStatusForUser(userId),
popup = lastMission?.let { getPopupStatus(it) } ?: PopupStatus(exists = false, type = null),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Create a popup state for the final level-up

For a user who completed Lv10 after already confirming the popup on the Lv10 mission, this reuses that completed mission's existing levelUpPopupShown=true, so the max-level response never exposes a LEVEL_UP popup for reaching level 10. Earlier level-ups work because awardNextLevel creates a new mission with levelUpPopupShown=false; at the cap no next mission is created, so the final level-up needs its own persisted/derived popup state before getPopupStatus can report it.

Useful? React with 👍 / 👎.

)
}

val userMission =
userMissionRepository.findCurrentMissionByUserId(userId) ?: throw CurrentMissionNotFoundException(userId)
val missionLevel = userLevel.currentLevel + 1
val mission =
missionRepository.findByLevel(missionLevel) ?: throw MissionDefinitionNotFoundException(missionLevel)

val missionInfo =
MissionInfo(
Expand Down Expand Up @@ -86,7 +95,10 @@ class MissionQueryService(
if (mission.missionType != MissionType.CONSECUTIVE_WEEK) {
return null
}
return buildWeeklyStatusForUser(userId)
}

private fun buildWeeklyStatusForUser(userId: UUID): WeeklyStatus {
val weekStartDate = getWeekStartDate(ServiceTime.today())
val from = ServiceTime.startOfDayUtc(weekStartDate)
val to = ServiceTime.startOfDayUtc(weekStartDate.plusWeeks(1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class UserMissionService(
override fun confirmLevelUp(userId: UUID) {
val userMission =
userMissionRepository.findCurrentMissionByUserId(userId)
?: userMissionRepository.findByUserId(userId).firstOrNull()
?: throw CurrentMissionNotFoundException(userId)

userMission.levelUpPopupShown = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.didit.application.achievement.dto

data class CurrentMissionResponse(
val currentLevel: Int,
val mission: MissionInfo,
val mission: MissionInfo?,
val weeklyStatus: WeeklyStatus?,
val popup: PopupStatus,
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.didit.application.achievement

import com.didit.application.achievement.exception.CurrentMissionNotFoundException
import com.didit.application.achievement.exception.MissionDefinitionNotFoundException
import com.didit.application.achievement.exception.UserLevelNotFoundException
import com.didit.application.achievement.required.MissionRepository
import com.didit.application.achievement.required.UserLevelRepository
Expand Down Expand Up @@ -74,10 +73,10 @@ class MissionQueryServiceTest {
val result = missionQueryService.getCurrentMission(userId)

assertThat(result.currentLevel).isEqualTo(0)
assertThat(result.mission.type).isEqualTo("FIRST_RETRO")
assertThat(result.mission.progress).isEqualTo(0)
assertThat(result.mission.target).isEqualTo(1)
assertThat(result.mission.remainingDays).isNull()
assertThat(result.mission!!.type).isEqualTo("FIRST_RETRO")
assertThat(result.mission!!.progress).isEqualTo(0)
assertThat(result.mission!!.target).isEqualTo(1)
assertThat(result.mission!!.remainingDays).isNull()
assertThat(result.weeklyStatus).isNull()
assertThat(result.popup.exists).isFalse()
}
Expand All @@ -103,8 +102,8 @@ class MissionQueryServiceTest {
val result = missionQueryService.getCurrentMission(userId)

assertThat(result.currentLevel).isEqualTo(1)
assertThat(result.mission.type).isEqualTo("TIME_LIMITED")
assertThat(result.mission.remainingDays).isEqualTo(5)
assertThat(result.mission!!.type).isEqualTo("TIME_LIMITED")
assertThat(result.mission!!.remainingDays).isEqualTo(5)
assertThat(result.weeklyStatus).isNull()
}

Expand All @@ -130,7 +129,7 @@ class MissionQueryServiceTest {
val result = missionQueryService.getCurrentMission(userId)

assertThat(result.currentLevel).isEqualTo(2)
assertThat(result.mission.type).isEqualTo("CONSECUTIVE_WEEK")
assertThat(result.mission!!.type).isEqualTo("CONSECUTIVE_WEEK")
assertThat(result.weeklyStatus).isNotNull()
assertThat(result.weeklyStatus!!.days).hasSize(7)
}
Expand All @@ -147,23 +146,31 @@ class MissionQueryServiceTest {
fun `현재 미션이 없으면 CurrentMissionNotFoundException을 발생시킨다`() {
val userLevel = UserLevel(userId = userId, currentLevel = 1)
whenever(userLevelRepository.findByUserId(userId)).thenReturn(userLevel)
whenever(missionRepository.findByLevel(2)).thenReturn(Mission.timeLimited())
whenever(userMissionRepository.findCurrentMissionByUserId(userId)).thenReturn(null)

assertThatThrownBy { missionQueryService.getCurrentMission(userId) }
.isInstanceOf(CurrentMissionNotFoundException::class.java)
}

@Test
fun `미션 정의가 없으면 MissionDefinitionNotFoundException을 발생시킨다`() {
val userLevel = UserLevel(userId = userId, currentLevel = 0)
val userMission = UserMission(userId = userId, missionId = UUID.randomUUID())
fun `최고 레벨(미션 정의 없음)이면 mission이 null이고 주간 현황을 반환한다`() {
val userLevel = UserLevel(userId = userId, currentLevel = 10)

whenever(userLevelRepository.findByUserId(userId)).thenReturn(userLevel)
whenever(userMissionRepository.findCurrentMissionByUserId(userId)).thenReturn(userMission)
whenever(missionRepository.findByLevel(1)).thenReturn(null)
whenever(missionRepository.findByLevel(11)).thenReturn(null)
whenever(userMissionRepository.findByUserId(userId)).thenReturn(emptyList())
whenever(retrospectiveRepository.findCompletedByUserIdAndPeriod(eq(userId), any(), any())).thenReturn(
emptyList(),
)

assertThatThrownBy { missionQueryService.getCurrentMission(userId) }
.isInstanceOf(MissionDefinitionNotFoundException::class.java)
val result = missionQueryService.getCurrentMission(userId)

assertThat(result.currentLevel).isEqualTo(10)
assertThat(result.mission).isNull()
assertThat(result.weeklyStatus).isNotNull()
assertThat(result.weeklyStatus!!.days).hasSize(7)
assertThat(result.popup.exists).isFalse()
}

@Test
Expand Down
Loading