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 @@ -22,6 +22,14 @@ fun WeeklyReportDto.toDomain(): WeeklyReport =
/** `week[].type` 중 일기를 뜻하는 값. 나머지(`DAILY_QUESTION`·`DEEP_THOUGHT`·미래 종류)는 일기가 아니다. */
private const val WEEK_RECORD_TYPE_DIARY = "DIARY"

/**
* 기록일수에서 제외할 종류.
*
* 깊은 생각은 기획에서 제거됐다. 서버는 `week[]` 에 계속 실어 보내지만 앱은 이 기능을
* 없는 것으로 다루므로 기록일수에도 세지 않는다 (#590).
*/
private val WEEK_RECORD_TYPES_NOT_COUNTED = setOf("DEEP_THOUGHT")

/**
* 와이어의 `type` 문자열을 도메인 의미(`isDiary`)로 접는다.
*
Expand All @@ -33,6 +41,7 @@ fun WeeklyReportDayDto.toDomain(): WeeklyReportDay =
diaryId = diaryId,
day = day,
isDiary = type.equals(WEEK_RECORD_TYPE_DIARY, ignoreCase = true),
countsAsRecord = WEEK_RECORD_TYPES_NOT_COUNTED.none { it.equals(type, ignoreCase = true) },
emotion = emotion?.toDomain(),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ data class WeeklyReportDay(
val diaryId: Long,
val day: Int,
val isDiary: Boolean,
/**
* 기록일수에 세는 종류인지 (#590).
*
* `week[]` 는 일기 외의 종류도 싣는다. 데일리질문은 세지만 **깊은 생각은 기획에서
* 제거된 기능이라 세지 않는다** — 서버가 계속 내려줘도 무시한다.
*/
val countsAsRecord: Boolean,
val emotion: TodayMood?,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ internal fun resolveDateInWeekOrNull(
*/
internal data class DailyRecordSummary(
val isDiary: Boolean,
/** 그 날짜에 **기록일수로 셀 수 있는** 기록이 하나라도 있었는지 (#590). */
val countsAsRecord: Boolean,
val emotion: TodayMood?,
)

Expand All @@ -56,6 +58,7 @@ internal fun aggregateWeekRecordsByDate(
.mapValues { (_, days) ->
DailyRecordSummary(
isDiary = days.any { it.isDiary },
countsAsRecord = days.any { it.countsAsRecord },
emotion =
days
.filter { it.emotion != null }
Expand All @@ -65,21 +68,30 @@ internal fun aggregateWeekRecordsByDate(
}

/**
* 기록일수 = 일기가 있는 날 + 주간 범위 내 데일리질문 답변 날짜의 합집합(중복 제거).
* 기록일수 = `week[]` 에 기록이 있는 날 + 주간 범위 내 데일리질문 답변 날짜의 합집합(중복 제거).
*
* 두 출처를 모두 `LocalDate` 로 복원한 뒤 합쳐야 같은 날의 일기·데일리질문이
* `distinct()` 로 1일로 접힌다.
*
* **일기만 세지 않는다.** 종전에는 `isDiary` 인 날만 셌는데, `week[]` 는 일기 외에
* `DAILY_QUESTION` 원소도 싣는다 (실서버 대조 2026-08-23, #590). 문구가 세는 것은
* "일기를 쓴 날" 이 아니라 "마음을 기록한 날" 이다.
*
* 다만 **깊은 생각은 제외한다** — 기획에서 제거된 기능이라 서버가 계속 내려줘도 앱은
* 없는 것으로 다룬다. 그 판단은 매퍼가 [WeeklyReportDay.countsAsRecord] 로 접어 둔다.
*
* (`isDiary` 는 계속 달력 점 표시에만 쓴다 — [aggregateWeekRecordsByDate].)
*/
internal fun countRecordedDays(
monday: LocalDate,
week: List<WeeklyReportDay>,
dailyQuestionDates: List<LocalDate>,
): Int {
val sunday = monday.plusDays(WEEK_LENGTH - 1L)
val diaryDates =
val recordDates =
aggregateWeekRecordsByDate(monday, week)
.filterValues { it.isDiary }
.filterValues { it.countsAsRecord }
.keys
val questionDatesInWeek = dailyQuestionDates.filter { it in monday..sunday }
return (diaryDates + questionDatesInWeek).distinct().size
return (recordDates + questionDatesInWeek).distinct().size
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class WeeklyReportRecordedDaysTest {
}

@Test
fun `isDiary 가 false 인 원소는 일기 기록일로 세지 않는다`() {
fun `일기가 아닌 기록도 기록일로 센다`() {
// #590 회귀 — week[] 는 일기 외 종류도 싣는다. isDiary 로 거르면 그 날이 사라진다.
val monday = LocalDate.of(2026, 7, 27)

val recordedDays =
Expand All @@ -100,9 +101,83 @@ class WeeklyReportRecordedDaysTest {
dailyQuestionDates = emptyList(),
)

assertEquals(1, recordedDays)
}

@Test
fun `깊은 생각만 있는 날은 세지 않는다`() {
// 기획에서 제거된 기능이라 서버가 week[] 에 계속 실어 보내도 앱은 없는 것으로 다룬다.
val monday = LocalDate.of(2026, 7, 27)

val recordedDays =
countRecordedDays(
monday = monday,
week = listOf(diaryDay(day = 28, isDiary = false, countsAsRecord = false)),
dailyQuestionDates = emptyList(),
)

assertEquals(0, recordedDays)
}

@Test
fun `같은 날에 깊은 생각과 일기가 함께 있으면 센다`() {
// 제외 대상은 "깊은 생각뿐인 날" 이지 그 날짜 자체가 아니다.
val monday = LocalDate.of(2026, 7, 27)

val recordedDays =
countRecordedDays(
monday = monday,
week =
listOf(
diaryDay(day = 28, isDiary = false, countsAsRecord = false),
diaryDay(day = 28),
),
dailyQuestionDates = emptyList(),
)

assertEquals(1, recordedDays)
}

@Test
fun `일기가 아닌 기록도 종류를 가리지 않고 날짜 단위로 센다`() {
// 문구가 세는 것은 "일기를 쓴 날" 이 아니라 "마음을 기록한 날" 이다.
val monday = LocalDate.of(2026, 7, 27)

val recordedDays =
countRecordedDays(
monday = monday,
week = listOf(diaryDay(day = 28, isDiary = false), diaryDay(day = 30)),
dailyQuestionDates = emptyList(),
)

assertEquals(2, recordedDays)
}

@Test
fun `week 의 비일기 기록과 같은 날 데일리질문은 1일로 접힌다`() {
// 두 출처가 같은 날을 가리키면 중복 제거된다 — 종류를 안 가려도 이중 계산은 없다.
val monday = LocalDate.of(2026, 7, 27)

val recordedDays =
countRecordedDays(
monday = monday,
week = listOf(diaryDay(day = 28, isDiary = false)),
dailyQuestionDates = listOf(LocalDate.of(2026, 7, 28)),
)

assertEquals(1, recordedDays)
}

@Test
fun `종류를 가리지 않아도 달력 점은 여전히 일기만 찍는다`() {
// isDiary 는 집계에서 빠졌을 뿐 표시 규칙에는 그대로 남는다.
val monday = LocalDate.of(2026, 7, 27)

val byDate = aggregateWeekRecordsByDate(monday, listOf(diaryDay(day = 28, isDiary = false)))

assertEquals(false, byDate.getValue(LocalDate.of(2026, 7, 28)).isDiary)
}

@Test
fun `빈 week 응답은 데일리질문만으로 집계된다`() {
val monday = LocalDate.of(2026, 7, 27)
Expand Down Expand Up @@ -135,8 +210,8 @@ class WeeklyReportRecordedDaysTest {
val monday = LocalDate.of(2026, 7, 27)
val week =
listOf(
WeeklyReportDay(diaryId = 1, day = 28, isDiary = false, emotion = null),
WeeklyReportDay(diaryId = 2, day = 28, isDiary = true, emotion = TodayMood.SAD),
WeeklyReportDay(diaryId = 1, day = 28, isDiary = false, countsAsRecord = true, emotion = null),
WeeklyReportDay(diaryId = 2, day = 28, isDiary = true, countsAsRecord = true, emotion = TodayMood.SAD),
)

val byDate = aggregateWeekRecordsByDate(monday, week)
Expand All @@ -152,8 +227,8 @@ class WeeklyReportRecordedDaysTest {
// 명세에 week[] 순서 의미도 같은 날짜 복수 원소의 병합 규칙도 없다 (Afternote-BE#131).
// 순서에 의존하면 서버가 정렬만 바꿔도 화면 이모지가 바뀐다 — diaryId 최대값으로 고정한다.
val monday = LocalDate.of(2026, 7, 27)
val older = WeeklyReportDay(diaryId = 1, day = 28, isDiary = true, emotion = TodayMood.HAPPY)
val newer = WeeklyReportDay(diaryId = 2, day = 28, isDiary = true, emotion = TodayMood.SAD)
val older = WeeklyReportDay(diaryId = 1, day = 28, isDiary = true, countsAsRecord = true, emotion = TodayMood.HAPPY)
val newer = WeeklyReportDay(diaryId = 2, day = 28, isDiary = true, countsAsRecord = true, emotion = TodayMood.SAD)

val ascending = aggregateWeekRecordsByDate(monday, listOf(older, newer))
val descending = aggregateWeekRecordsByDate(monday, listOf(newer, older))
Expand All @@ -167,8 +242,8 @@ class WeeklyReportRecordedDaysTest {
val monday = LocalDate.of(2026, 7, 27)
val week =
listOf(
WeeklyReportDay(diaryId = 9, day = 28, isDiary = true, emotion = null),
WeeklyReportDay(diaryId = 1, day = 28, isDiary = false, emotion = TodayMood.HAPPY),
WeeklyReportDay(diaryId = 9, day = 28, isDiary = true, countsAsRecord = true, emotion = null),
WeeklyReportDay(diaryId = 1, day = 28, isDiary = false, countsAsRecord = true, emotion = TodayMood.HAPPY),
)

val byDate = aggregateWeekRecordsByDate(monday, week)
Expand All @@ -188,11 +263,13 @@ class WeeklyReportRecordedDaysTest {
private fun diaryDay(
day: Int,
isDiary: Boolean = true,
countsAsRecord: Boolean = true,
): WeeklyReportDay =
WeeklyReportDay(
diaryId = day.toLong(),
day = day,
isDiary = isDiary,
countsAsRecord = countsAsRecord,
emotion = TodayMood.HAPPY,
)
}
Loading