Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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,6 +5,7 @@ import noweekend.core.api.controller.v1.request.LeaveInputRequest
import noweekend.core.api.controller.v1.request.ProfileRequest
import noweekend.core.api.controller.v1.request.TagRequest
import noweekend.core.api.controller.v1.response.DefaultTags
import noweekend.core.api.controller.v1.response.OnboardingStatusResponse
import noweekend.core.api.security.annotations.CurrentUserId
import noweekend.core.domain.user.UserService
import noweekend.core.support.response.ApiResponse
Expand Down Expand Up @@ -62,4 +63,13 @@ class OnboardingController(
"연차 정보가 성공적으로 저장되었습니다.",
)
}

@GetMapping("/onboarding/status")
override fun getOnboardingStatus(
@CurrentUserId userId: String,
): ApiResponse<OnboardingStatusResponse> {
return ApiResponse.success(
userService.getOnboardingStatus(userId),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import noweekend.core.api.controller.v1.request.LeaveInputRequest
import noweekend.core.api.controller.v1.request.ProfileRequest
import noweekend.core.api.controller.v1.request.TagRequest
import noweekend.core.api.controller.v1.response.DefaultTags
import noweekend.core.api.controller.v1.response.OnboardingStatusResponse
import noweekend.core.api.security.annotations.CurrentUserId
import noweekend.core.support.response.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponse as SwaggerApiResponse
Expand Down Expand Up @@ -340,4 +341,72 @@ interface OnboardingControllerDocs {
fun getDefaultTag(
@Parameter(hidden = true) @CurrentUserId userId: String,
): ApiResponse<DefaultTags>

@Operation(
summary = "온보딩: 사용자 진행 상태 조회",
description = """
온보딩 플로우(이름/생년월일, 연차, 일정 태그) 입력 진행 상황을 반환합니다.

status 값 설명:
- NONE: 이름/생년월일 미입력 상태
- NAME_AND_BIRTHDAY: 이름/생년월일 입력만 완료
- ANNUAL_LEAVE: 연차 입력까지 완료
- DONE: 모든 온보딩 절차 완료
""",
responses = [
SwaggerApiResponse(
responseCode = "200",
description = "온보딩 상태 조회 성공",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ApiResponse::class),
examples = [
ExampleObject(
name = "예시 응답",
value = """
{
"result": "SUCCESS",
"data": {
"status": "DONE"
},
"error": null
}
""",
),
],
),
],
),
SwaggerApiResponse(
responseCode = "400",
description = "온보딩 순서에 맞지 않게 데이터가 입력된 경우",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ApiResponse::class),
examples = [
ExampleObject(
name = "온보딩 순서 오류 예시",
value = """
{
"result": "ERROR",
"data": null,
"error": {
"code": "INVALID_ONBOARD_STATUS",
"message": "온보딩 순서에 맞게 요청하지 않은 상태입니다. 사용자 정보조회하여 어떤 값이 입력되지 않았는지 확인해주세요.",
"data": {}
}
}
""",
),
],
),
],
),
],
)
fun getOnboardingStatus(
@Parameter(hidden = true) @CurrentUserId userId: String,
): ApiResponse<OnboardingStatusResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package noweekend.core.api.controller.v1.response

import io.swagger.v3.oas.annotations.media.Schema

@Schema(description = "온보딩 진행 상태 응답")
data class OnboardingStatusResponse(
@Schema(
description = "온보딩 단계 상태 (NONE: 이름/생년월일 미입력, NAME_AND_BIRTHDAY: 이름/생년월일만 입력, ANNUAL_LEAVE: 연차까지 입력, DONE: 모든 온보딩 완료)",
example = "ANNUAL_LEAVE",
)
val status: OnboardingStatus,
)

@Schema(description = "온보딩 상태 Enum")
enum class OnboardingStatus {
@Schema(description = "이름/생년월일 입력 전 상태")
NONE,

@Schema(description = "이름/생년월일 입력 완료 상태")
NAME_AND_BIRTHDAY,

@Schema(description = "연차 입력까지 완료된 상태")
ANNUAL_LEAVE,

@Schema(description = "모든 온보딩 완료 상태")
DONE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import noweekend.core.api.controller.v1.request.LeaveInputRequest
import noweekend.core.api.controller.v1.request.LocationRequest
import noweekend.core.api.controller.v1.request.ProfileRequest
import noweekend.core.api.controller.v1.request.TagUpdateRequest
import noweekend.core.api.controller.v1.response.OnboardingStatusResponse
import noweekend.core.api.controller.v1.response.UserInformationResponse
import noweekend.core.domain.tag.BasicTag
import noweekend.core.domain.tag.UserTags
Expand All @@ -17,4 +18,5 @@ interface UserService {
fun getStateTags(userId: String): UserTags
fun updateLocation(request: LocationRequest, userId: String)
fun getUserInformationById(userId: String): UserInformationResponse
fun getOnboardingStatus(userId: String): OnboardingStatusResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import noweekend.core.api.controller.v1.request.LeaveInputRequest
import noweekend.core.api.controller.v1.request.LocationRequest
import noweekend.core.api.controller.v1.request.ProfileRequest
import noweekend.core.api.controller.v1.request.TagUpdateRequest
import noweekend.core.api.controller.v1.response.OnboardingStatus
import noweekend.core.api.controller.v1.response.OnboardingStatusResponse
import noweekend.core.api.controller.v1.response.UserInformationResponse
import noweekend.core.domain.enumerate.ScheduleCategory
import noweekend.core.domain.schedule.ScheduleReader
Expand Down Expand Up @@ -66,7 +68,7 @@ class UserServiceImpl(
daysToAdd += 0.5
}
val updatedUser = user.copy(
remainingAnnualLeave = user.remainingAnnualLeave + daysToAdd,
remainingAnnualLeave = (user.remainingAnnualLeave ?: 0.0) + daysToAdd,
)
userWriter.upsert(updatedUser)
}
Expand Down Expand Up @@ -119,4 +121,36 @@ class UserServiceImpl(

return UserInformationResponse.of(user, averageTemperature)
}

override fun getOnboardingStatus(userId: String): OnboardingStatusResponse {
val user = userReader.findUserById(userId) ?: throw CoreException(ErrorType.USER_NOT_FOUND_INTERNAL)

val nameAndBirthdayEntered = user.birthDate != null && user.name != null
val annualLeaveEntered = user.remainingAnnualLeave != null
val userTags = tagReader.getUserTags(userId)
val selectedTagsCount = (userTags.selectedBasicTags + userTags.selectedCustomTags + userTags.selectedCustomTags + userTags.unselectedCustomTags).count { it.selected }
val tagEntered = selectedTagsCount > 0

// 1. 이름/생년월일 안됨 -> NONE
if (!nameAndBirthdayEntered && (!annualLeaveEntered || !tagEntered)) {
return OnboardingStatusResponse(OnboardingStatus.NONE)
}

// 2. 이름/생년월일만 됨 -> NAME_AND_BIRTHDAY
if (nameAndBirthdayEntered && !annualLeaveEntered && !tagEntered) {
return OnboardingStatusResponse(OnboardingStatus.NAME_AND_BIRTHDAY)
}

// 3. 연차까지 됨(이름/생년월일, 연차 ok, 태그 없음) -> ANNUAL_LEAVE
if (nameAndBirthdayEntered && annualLeaveEntered && !tagEntered) {
return OnboardingStatusResponse(OnboardingStatus.ANNUAL_LEAVE)
}

// 4. 모든게 정상적으로 입력됨 -> DONE
if (nameAndBirthdayEntered && annualLeaveEntered) {
return OnboardingStatusResponse(OnboardingStatus.DONE)
}

throw CoreException(ErrorType.INVALID_ONBOARD_STATUS)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ enum class ErrorType(
MCP_SERVER_SANDWICH_ERROR(HttpStatus.GATEWAY_TIMEOUT, ErrorCode.E504, "MCP 추천 서버의 응답이 없습니다. 잠시 후 다시 시도해주세요.", LogLevel.ERROR),
INVALID_LOCATION(HttpStatus.BAD_REQUEST, ErrorCode.E400, "사용자가 한국 위치가 아니기 때문에 날씨를 추천할 수 없습니다.", LogLevel.WARN),
INVALID_SCHEDULE_TAG(HttpStatus.BAD_REQUEST, ErrorCode.E400, "유효하지 않은 태그가 포함되어 있습니다.", LogLevel.WARN),
INVALID_ONBOARD_STATUS(HttpStatus.BAD_REQUEST, ErrorCode.E400, "온보딩 순서에 맞게 요청하지 않은 상태입니다. 사용자 정보조회하여 어떤 값이 입력되지 않았는지 확인해주세요.", LogLevel.WARN),
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ data class User(
val revocableToken: String?,
val role: Role,
val birthDate: LocalDate?,
val remainingAnnualLeave: Double = 0.0,
val remainingAnnualLeave: Double?,
val createdAt: LocalDateTime?,
val updatedAt: LocalDateTime?,
var deleted: Boolean,
Expand All @@ -41,6 +41,7 @@ data class User(
revocableToken = revocableToken,
role = role,
birthDate = null,
remainingAnnualLeave = null,
createdAt = null,
updatedAt = null,
deleted = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class UserEntity(
val birthDate: LocalDate? = null,

@Column(name = "remaining_annual_leave", nullable = true)
val remainingAnnualLeave: Double = 0.0,
val remainingAnnualLeave: Double?,

@Embedded
var location: LocationEmbeddable?,
Expand Down