Skip to content

Commit f3941b2

Browse files
authored
Add new REST controllers for profile, payroll, and work policy management (#16)
1 parent e896804 commit f3941b2

15 files changed

+166
-34
lines changed

src/main/kotlin/com/moa/common/auth/AuthMemberResolver.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ class AuthMemberResolver(
5555
} ?: throw UnauthorizedException()
5656
}
5757

58-
private fun validateOnboardingCompleted(memberId: Long) {
59-
val today = LocalDate.now()
60-
58+
private fun validateOnboardingCompleted(memberId: Long, today: LocalDate = LocalDate.now()) {
6159
val profileCompleted = isProfileCompleted(memberId)
6260
val payrollCompleted = isPayrollCompleted(memberId, today)
6361
val workPolicyCompleted = isWorkPolicyCompleted(memberId, today)

src/main/kotlin/com/moa/common/exception/ErrorCode.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@ enum class ErrorCode(
77
// 5xx
88
INTERNAL_SERVER_ERROR("INTERNAL_SERVER_ERROR", "서버 내부 오류가 발생했습니다"),
99

10-
// 4xx
10+
// 400
1111
BAD_REQUEST("BAD_REQUEST", "잘못된 요청입니다."),
12-
UNAUTHORIZED("UNAUTHORIZED", "인증되지 않은 사용자입니다"),
13-
ONBOARDING_INCOMPLETE("ONBOARDING_INCOMPLETE", "온보딩이 완료되지 않았습니다"),
14-
RESOURCE_NOT_FOUND("RESOURCE_NOT_FOUND", "리소스를 찾을 수 없습니다"),
15-
1612
INVALID_PAYROLL_INPUT("INVALID_PAYROLL_INPUT", "급여 입력값이 유효하지 않습니다"),
1713
INVALID_WORK_POLICY_INPUT("INVALID_WORK_POLICY_INPUT", "근무정책 입력값이 유효하지 않습니다"),
18-
WORKDAY_NOT_FOUND("WORKDAY_NOT_FOUND", "해당 날짜의 근무 정보를 찾을 수 없습니다"),
1914
REQUIRED_TERMS_MUST_BE_AGREED("REQUIRED_TERMS_MUST_BE_AGREED", "필수 약관은 동의해야 합니다"),
2015
REQUIRED_MARKETING_TERM("REQUIRED_MARKETING_TERM", "마케팅 약관 동의가 필요합니다."),
2116

17+
// 401
18+
UNAUTHORIZED("UNAUTHORIZED", "인증되지 않은 사용자입니다"),
2219
INVALID_ID_TOKEN("INVALID_ID_TOKEN", "유효하지 않은 ID 토큰입니다"),
20+
21+
// 403
22+
ONBOARDING_INCOMPLETE("ONBOARDING_INCOMPLETE", "온보딩이 완료되지 않았습니다"),
2323
EXPIRED_TOKEN("EXPIRED_TOKEN", "토큰이 만료되었습니다"),
24+
25+
// 404
26+
RESOURCE_NOT_FOUND("RESOURCE_NOT_FOUND", "리소스를 찾을 수 없습니다"),
2427
}

src/main/kotlin/com/moa/common/oidc/OidcPublicKeyCache.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.moa.common.oidc
22

33
import com.moa.common.exception.BadRequestException
44
import com.moa.common.exception.ErrorCode
5+
import com.moa.common.exception.UnauthorizedException
56
import org.springframework.stereotype.Component
67
import java.security.interfaces.RSAPublicKey
78
import java.time.Instant
@@ -35,6 +36,6 @@ class OidcPublicKeyCache(
3536
cache[jwksUri] = CacheEntry(keys, expiresAt)
3637

3738
return keys[kid]
38-
?: throw BadRequestException(ErrorCode.INVALID_ID_TOKEN);
39+
?: throw UnauthorizedException(ErrorCode.INVALID_ID_TOKEN);
3940
}
4041
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.moa.controller
2+
3+
import com.moa.common.auth.Auth
4+
import com.moa.common.auth.AuthMemberInfo
5+
import com.moa.common.response.ApiResponse
6+
import com.moa.service.PayrollService
7+
import com.moa.service.dto.PayrollUpsertRequest
8+
import jakarta.validation.Valid
9+
import org.springframework.web.bind.annotation.*
10+
11+
@RestController
12+
@RequestMapping("/api/v1/payroll")
13+
class PayrollController(
14+
private val payrollService: PayrollService,
15+
) {
16+
17+
@GetMapping
18+
fun getPayroll(@Auth member: AuthMemberInfo) =
19+
ApiResponse.success(payrollService.getCurrent(member.id))
20+
21+
@PatchMapping
22+
fun upsertPayroll(
23+
@Auth member: AuthMemberInfo,
24+
@RequestBody @Valid req: PayrollUpsertRequest,
25+
) = ApiResponse.success(payrollService.upsert(member.id, req))
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.moa.controller
2+
3+
import com.moa.common.auth.Auth
4+
import com.moa.common.auth.AuthMemberInfo
5+
import com.moa.common.response.ApiResponse
6+
import com.moa.service.ProfileService
7+
import com.moa.service.dto.NicknameUpdateRequest
8+
import jakarta.validation.Valid
9+
import org.springframework.web.bind.annotation.*
10+
11+
@RestController
12+
@RequestMapping("/api/v1/profile")
13+
class ProfileController(
14+
private val profileService: ProfileService,
15+
) {
16+
17+
@GetMapping
18+
fun getProfile(@Auth member: AuthMemberInfo) =
19+
ApiResponse.success(profileService.getProfile(member.id))
20+
21+
@PatchMapping("/nickname")
22+
fun updateNickname(
23+
@Auth member: AuthMemberInfo,
24+
@RequestBody @Valid req: NicknameUpdateRequest,
25+
) = ApiResponse.success(profileService.updateNickname(member.id, req))
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.moa.controller
2+
3+
import com.moa.common.auth.Auth
4+
import com.moa.common.auth.AuthMemberInfo
5+
import com.moa.common.response.ApiResponse
6+
import com.moa.service.WorkPolicyService
7+
import com.moa.service.dto.WorkPolicyUpsertRequest
8+
import jakarta.validation.Valid
9+
import org.springframework.web.bind.annotation.*
10+
11+
@RestController
12+
@RequestMapping("/api/v1/work-policy")
13+
class WorkPolicyController(
14+
private val workPolicyService: WorkPolicyService,
15+
) {
16+
17+
@GetMapping
18+
fun getWorkPolicy(@Auth member: AuthMemberInfo) =
19+
ApiResponse.success(workPolicyService.getCurrent(member.id))
20+
21+
@PatchMapping
22+
fun upsertWorkPolicy(
23+
@Auth member: AuthMemberInfo,
24+
@RequestBody @Valid req: WorkPolicyUpsertRequest,
25+
) = ApiResponse.success(workPolicyService.upsert(member.id, req))
26+
}

src/main/kotlin/com/moa/service/PayrollService.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ package com.moa.service
22

33
import com.moa.common.exception.BadRequestException
44
import com.moa.common.exception.ErrorCode
5+
import com.moa.common.exception.NotFoundException
56
import com.moa.entity.PayrollVersion
67
import com.moa.repository.PayrollVersionRepository
78
import com.moa.service.dto.PayrollResponse
89
import com.moa.service.dto.PayrollUpsertRequest
910
import org.springframework.stereotype.Service
1011
import org.springframework.transaction.annotation.Transactional
12+
import java.time.LocalDate
1113

1214
@Service
1315
class PayrollService(
1416
private val payrollVersionRepository: PayrollVersionRepository,
1517
) {
1618

1719
@Transactional
18-
fun upsert(memberId: Long, req: PayrollUpsertRequest): PayrollResponse {
19-
20-
val effectiveFrom = req.effectiveFrom
20+
fun upsert(memberId: Long, req: PayrollUpsertRequest, today: LocalDate = LocalDate.now()): PayrollResponse {
2121
val salaryInputType = req.salaryInputType
2222
val salaryAmount = req.salaryAmount
2323
val paydayDay = req.paydayDay ?: 25
@@ -26,7 +26,7 @@ class PayrollService(
2626
throw BadRequestException(ErrorCode.INVALID_PAYROLL_INPUT)
2727
}
2828

29-
val saved = payrollVersionRepository.findByMemberIdAndEffectiveFrom(memberId, effectiveFrom)
29+
val saved = payrollVersionRepository.findByMemberIdAndEffectiveFrom(memberId, today)
3030
?.apply {
3131
this.salaryInputType = salaryInputType
3232
this.salaryAmount = salaryAmount
@@ -35,7 +35,7 @@ class PayrollService(
3535
?: payrollVersionRepository.save(
3636
PayrollVersion(
3737
memberId = memberId,
38-
effectiveFrom = effectiveFrom,
38+
effectiveFrom = today,
3939
salaryInputType = salaryInputType,
4040
salaryAmount = salaryAmount,
4141
paydayDay = paydayDay,
@@ -49,4 +49,17 @@ class PayrollService(
4949
paydayDay = saved.paydayDay,
5050
)
5151
}
52+
53+
@Transactional(readOnly = true)
54+
fun getCurrent(memberId: Long, today: LocalDate = LocalDate.now()): PayrollResponse {
55+
val version = payrollVersionRepository
56+
.findTopByMemberIdAndEffectiveFromLessThanEqualOrderByEffectiveFromDesc(memberId, today)
57+
?: throw NotFoundException()
58+
return PayrollResponse(
59+
effectiveFrom = version.effectiveFrom,
60+
salaryInputType = version.salaryInputType,
61+
salaryAmount = version.salaryAmount,
62+
paydayDay = version.paydayDay,
63+
)
64+
}
5265
}

src/main/kotlin/com/moa/service/ProfileService.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.moa.service
22

3+
import com.moa.common.exception.NotFoundException
34
import com.moa.entity.Profile
45
import com.moa.repository.ProfileRepository
6+
import com.moa.service.dto.NicknameUpdateRequest
57
import com.moa.service.dto.ProfileResponse
68
import com.moa.service.dto.OnboardingProfileUpsertRequest
79
import org.springframework.stereotype.Service
@@ -12,6 +14,16 @@ class ProfileService(
1214
private val profileRepository: ProfileRepository,
1315
) {
1416

17+
@Transactional(readOnly = true)
18+
fun getProfile(memberId: Long): ProfileResponse {
19+
val profile = profileRepository.findByMemberId(memberId)
20+
?: throw NotFoundException()
21+
return ProfileResponse(
22+
nickname = profile.nickname,
23+
workplace = profile.workplace,
24+
)
25+
}
26+
1527
@Transactional
1628
fun upsertProfile(memberId: Long, req: OnboardingProfileUpsertRequest): ProfileResponse {
1729
val nickname = req.nickname
@@ -30,4 +42,17 @@ class ProfileService(
3042
workplace = profile.workplace,
3143
)
3244
}
45+
46+
@Transactional
47+
fun updateNickname(memberId: Long, req: NicknameUpdateRequest): ProfileResponse {
48+
val profile = profileRepository.findByMemberId(memberId)
49+
?: throw NotFoundException()
50+
51+
profile.nickname = req.nickname
52+
53+
return ProfileResponse(
54+
nickname = profile.nickname,
55+
workplace = profile.workplace,
56+
)
57+
}
3358
}

src/main/kotlin/com/moa/service/WorkPolicyService.kt

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,26 @@ package com.moa.service
22

33
import com.moa.common.exception.BadRequestException
44
import com.moa.common.exception.ErrorCode
5+
import com.moa.common.exception.NotFoundException
56
import com.moa.entity.WorkPolicyVersion
67
import com.moa.repository.WorkPolicyVersionRepository
78
import com.moa.service.dto.WorkPolicyResponse
89
import com.moa.service.dto.WorkPolicyUpsertRequest
910
import org.springframework.stereotype.Service
1011
import org.springframework.transaction.annotation.Transactional
12+
import java.time.LocalDate
1113

1214
@Service
1315
class WorkPolicyService(
1416
private val versionRepository: WorkPolicyVersionRepository,
1517
) {
1618

1719
@Transactional
18-
fun upsert(memberId: Long, req: WorkPolicyUpsertRequest): WorkPolicyResponse {
19-
20-
validateRequest(req)
21-
22-
val version = versionRepository.findByMemberIdAndEffectiveFrom(memberId, req.effectiveFrom)
20+
fun upsert(memberId: Long, req: WorkPolicyUpsertRequest, today: LocalDate = LocalDate.now()): WorkPolicyResponse {
21+
val version = versionRepository.findByMemberIdAndEffectiveFrom(memberId, today)
2322
?: WorkPolicyVersion(
2423
memberId = memberId,
25-
effectiveFrom = req.effectiveFrom,
24+
effectiveFrom = today,
2625
clockInTime = req.clockInTime,
2726
clockOutTime = req.clockOutTime,
2827
workdays = req.workdays.toMutableSet(),
@@ -41,12 +40,17 @@ class WorkPolicyService(
4140
)
4241
}
4342

44-
private fun validateRequest(req: WorkPolicyUpsertRequest) {
45-
if (!req.clockInTime.isBefore(req.clockOutTime)) {
46-
throw BadRequestException(ErrorCode.INVALID_WORK_POLICY_INPUT)
47-
}
48-
if (req.workdays.isEmpty()) {
49-
throw BadRequestException(ErrorCode.INVALID_WORK_POLICY_INPUT)
50-
}
43+
@Transactional(readOnly = true)
44+
fun getCurrent(memberId: Long, today: LocalDate = LocalDate.now()): WorkPolicyResponse {
45+
val version = versionRepository
46+
.findTopByMemberIdAndEffectiveFromLessThanEqualOrderByEffectiveFromDesc(memberId, today)
47+
?: throw NotFoundException()
48+
49+
return WorkPolicyResponse(
50+
effectiveFrom = version.effectiveFrom,
51+
workdays = version.workdays.sortedBy { it.dayOfWeek.value },
52+
clockInTime = version.clockInTime,
53+
clockOutTime = version.clockOutTime,
54+
)
5155
}
5256
}

src/main/kotlin/com/moa/service/WorkdayService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ class WorkdayService(
9696
private fun findEffectivePolicyForWorkday(memberId: Long, date: LocalDate): WorkPolicyVersion {
9797
val policy = workPolicyVersionRepository
9898
.findTopByMemberIdAndEffectiveFromLessThanEqualOrderByEffectiveFromDesc(memberId, date)
99-
?: throw NotFoundException(ErrorCode.WORKDAY_NOT_FOUND)
99+
?: throw NotFoundException()
100100

101101
if (policy.workdays.none { it.dayOfWeek == date.dayOfWeek }) {
102-
throw NotFoundException(ErrorCode.WORKDAY_NOT_FOUND)
102+
throw NotFoundException()
103103
}
104104

105105
return policy

0 commit comments

Comments
 (0)