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
30 changes: 28 additions & 2 deletions src/main/kotlin/com/moa/entity/DailyEventType.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
package com.moa.entity

import java.time.LocalDate

/**
* 일자별로 표시할 부가 이벤트를 정의하는 열거형입니다.
*/
enum class DailyEventType {
PAYDAY,
HOLIDAY,
/** 급여일에 해당하는 경우 */
PAYDAY;

companion object {
/**
* 특정 일자와 급여일 설정을 바탕으로 해당 일자에 표시할 이벤트를 판정합니다.
*
* 현재는 급여일([PAYDAY])만 지원하며, 추후 공휴일 등 다른 이벤트가 추가될 수 있습니다.
*
* @param date 이벤트를 판정할 기준 일자
* @param paydayDay 사용자 설정 급여일
* @return 해당 일자에 적용되는 [DailyEventType] 목록
*/
fun resolve(date: LocalDate, paydayDay: PaydayDay): List<DailyEventType> {
val events = mutableListOf<DailyEventType>()

if (paydayDay.isPayday(date)) {
events += PAYDAY
}

return events
}
}
}
50 changes: 49 additions & 1 deletion src/main/kotlin/com/moa/entity/DailyWorkStatusType.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,55 @@
package com.moa.entity

import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime

/**
* 일자별 근무 진행 상태를 정의하는 열거형입니다.
*/
enum class DailyWorkStatusType {
/** 근무 일정이 없거나 상태를 판정할 수 없는 경우 */
NONE,

/** 아직 근무 종료 시각이 지나지 않은 경우 */
SCHEDULED,
COMPLETED,

/** 근무 종료 시각이 지나 완료된 경우 */
COMPLETED;

companion object {
/**
* 특정 일자의 근무 스케줄 정보를 바탕으로 근무 상태를 판정합니다.
*
* 근무 유형이 [DailyWorkScheduleType.NONE]이거나, 출근/퇴근 시간 중 하나라도 없으면
* 상태를 판정할 수 없으므로 [NONE]을 반환합니다.
* 퇴근 시간이 출근 시간보다 이른 경우에는 자정을 넘기는 근무로 간주하여 익일 종료 시각 기준으로 계산합니다.
*
* @param date 근무 상태를 판정할 기준 일자
* @param scheduleType 해당 일자의 근무 일정 유형
* @param clockIn 출근 시간
* @param clockOut 퇴근 시간
* @param now 상태 판정에 사용할 현재 시각. 기본값은 [LocalDateTime.now] 입니다.
* @return 판정된 [DailyWorkStatusType]. 종료 시각 이전이면 [SCHEDULED], 이후면 [COMPLETED]를 반환합니다.
*/
fun resolve(
date: LocalDate,
scheduleType: DailyWorkScheduleType,
clockIn: LocalTime?,
clockOut: LocalTime?,
now: LocalDateTime = LocalDateTime.now(),
): DailyWorkStatusType {
if (scheduleType == DailyWorkScheduleType.NONE) return NONE

val resolvedClockIn = clockIn ?: return NONE
val resolvedClockOut = clockOut ?: return NONE
val endAt = if (!resolvedClockOut.isBefore(resolvedClockIn)) {
date.atTime(resolvedClockOut)
} else {
date.plusDays(1).atTime(resolvedClockOut)
}

return if (now.isBefore(endAt)) SCHEDULED else COMPLETED
}
}
}
64 changes: 64 additions & 0 deletions src/main/kotlin/com/moa/entity/PaydayDay.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.moa.entity

import jakarta.persistence.Column
import jakarta.persistence.Embeddable
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.YearMonth

/**
* 사용자가 설정한 급여일을 표현하는 Value Object 입니다.
*
* 급여일은 1일부터 31일 사이의 값만 허용하며,
* 실제 급여일 계산 시 월말 보정 및 주말 보정 규칙을 함께 제공합니다.
*/
@Embeddable
data class PaydayDay(
@Column(name = "payday_day", nullable = false)
var value: Int = 25,
) {
init {
require(value in 1..31) { "paydayDay must be between 1 and 31" }
}

/**
* 특정 연월에 적용되는 실제 급여일을 계산합니다.
*
* 설정한 급여일이 해당 월에 없으면 말일로 보정하고,
* 보정된 날짜가 주말이면 직전 금요일로 당깁니다.
*
* @param year 급여일을 계산할 연도
* @param month 급여일을 계산할 월
* @return 실제 적용되는 급여일
*/
fun resolveEffectiveDate(year: Int, month: Int): LocalDate {
val yearMonth = YearMonth.of(year, month)
val baseDate = yearMonth.atDay(minOf(value, yearMonth.lengthOfMonth()))

return when (baseDate.dayOfWeek) {
DayOfWeek.SATURDAY -> baseDate.minusDays(1)
DayOfWeek.SUNDAY -> baseDate.minusDays(2)
else -> baseDate
}
}

/**
* 특정 날짜가 이 급여일 설정의 실제 급여일인지 판정합니다.
*
* @param date 판정할 날짜
* @return 해당 날짜가 실제 급여일이면 `true`
*/
fun isPayday(date: LocalDate): Boolean =
resolveEffectiveDate(date.year, date.monthValue) == date

companion object {
/**
* 특정 날짜에 실제 급여일로 귀결되는 모든 급여일 설정 값을 반환합니다.
*
* @param date 기준 날짜
* @return 해당 날짜를 실제 급여일로 가지는 [PaydayDay] 집합
*/
fun resolvingTo(date: LocalDate): Set<PaydayDay> =
(1..31).map(::PaydayDay).filterTo(mutableSetOf()) { it.isPayday(date) }
}
}
4 changes: 0 additions & 4 deletions src/main/kotlin/com/moa/entity/PayrollVersion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,3 @@ class PayrollVersion(
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0
}

enum class SalaryInputType {
ANNUAL, MONTHLY
}
4 changes: 2 additions & 2 deletions src/main/kotlin/com/moa/entity/Profile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class Profile(
@Column(nullable = true)
var workplace: String? = null,

@Column(nullable = false)
var paydayDay: Int = 25,
@Embedded
var paydayDay: PaydayDay = PaydayDay(),
) : BaseEntity() {

@Id
Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/com/moa/entity/SalaryInputType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.moa.entity

enum class SalaryInputType {
ANNUAL,
MONTHLY,
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.moa.entity
package com.moa.entity.notification

import com.moa.entity.BaseEntity
import jakarta.persistence.*
import java.time.LocalDate
import java.time.LocalTime
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.moa.entity
package com.moa.entity.notification

import com.moa.entity.BaseEntity
import jakarta.persistence.*

@Entity
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moa.entity
package com.moa.entity.notification

enum class NotificationSettingType(
val category: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moa.entity
package com.moa.entity.notification

enum class NotificationStatus {
PENDING,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moa.entity
package com.moa.entity.notification

enum class NotificationType(
val title: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.moa.repository

import com.moa.entity.NotificationLog
import com.moa.entity.NotificationStatus
import com.moa.entity.NotificationType
import com.moa.entity.notification.NotificationLog
import com.moa.entity.notification.NotificationStatus
import com.moa.entity.notification.NotificationType
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDate
import java.time.LocalTime
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.moa.repository

import com.moa.entity.NotificationSetting
import com.moa.entity.notification.NotificationSetting
import org.springframework.data.jpa.repository.JpaRepository

interface NotificationSettingRepository : JpaRepository<NotificationSetting, Long> {
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/moa/repository/ProfileRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import org.springframework.data.jpa.repository.JpaRepository

interface ProfileRepository : JpaRepository<Profile, Long> {
fun findByMemberId(memberId: Long): Profile?
fun findAllByPaydayDayIn(paydayDays: Collection<Int>): List<Profile>
fun findAllByPaydayDay_ValueIn(paydayDayValues: Collection<Int>): List<Profile>
}
2 changes: 1 addition & 1 deletion src/main/kotlin/com/moa/service/OnboardingStatusService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class OnboardingStatusService(
ProfileResponse(
nickname = it.nickname,
workplace = it.workplace,
paydayDay = it.paydayDay,
paydayDay = it.paydayDay.value,
)
}

Expand Down
17 changes: 0 additions & 17 deletions src/main/kotlin/com/moa/service/PaydayResolver.kt

This file was deleted.

21 changes: 11 additions & 10 deletions src/main/kotlin/com/moa/service/ProfileService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.moa.service
import com.moa.common.exception.BadRequestException
import com.moa.common.exception.ErrorCode
import com.moa.common.exception.NotFoundException
import com.moa.entity.PaydayDay
import com.moa.entity.Profile
import com.moa.repository.ProfileRepository
import com.moa.service.dto.NicknameUpdateRequest
Expand All @@ -25,7 +26,7 @@ class ProfileService(
return ProfileResponse(
nickname = profile.nickname,
workplace = profile.workplace,
paydayDay = profile.paydayDay,
paydayDay = profile.paydayDay.value,
)
}

Expand All @@ -45,7 +46,7 @@ class ProfileService(
return ProfileResponse(
nickname = profile.nickname,
workplace = profile.workplace,
paydayDay = profile.paydayDay,
paydayDay = profile.paydayDay.value,
)
}

Expand All @@ -59,7 +60,7 @@ class ProfileService(
return ProfileResponse(
nickname = profile.nickname,
workplace = profile.workplace,
paydayDay = profile.paydayDay,
paydayDay = profile.paydayDay.value,
)
}

Expand All @@ -73,25 +74,25 @@ class ProfileService(
return ProfileResponse(
nickname = profile.nickname,
workplace = profile.workplace,
paydayDay = profile.paydayDay,
paydayDay = profile.paydayDay.value,
)
}

@Transactional
fun updatePayday(memberId: Long, req: PaydayUpdateRequest): ProfileResponse {
if (req.paydayDay !in 1..31) {
throw BadRequestException(ErrorCode.INVALID_PAYDAY_INPUT)
}

val profile = profileRepository.findByMemberId(memberId)
?: throw NotFoundException()

profile.paydayDay = req.paydayDay
profile.paydayDay = try {
PaydayDay(req.paydayDay)
} catch (_: IllegalArgumentException) {
throw BadRequestException(ErrorCode.INVALID_PAYDAY_INPUT)
}

return ProfileResponse(
nickname = profile.nickname,
workplace = profile.workplace,
paydayDay = profile.paydayDay,
paydayDay = profile.paydayDay.value,
)
}
}
Loading
Loading