Skip to content
2 changes: 1 addition & 1 deletion src/main/kotlin/com/doki/global/security/JwtProvider.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.doki.global.security

import com.doki.user.auth.infrastructure.dto.response.TokenResponse
import com.doki.user.domain.vo.UserRole
import com.doki.user.user.domain.vo.UserRole
import io.jsonwebtoken.*
import io.jsonwebtoken.io.Decoders
import io.jsonwebtoken.security.Keys
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/doki/mypage/ui/MyPageApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.doki.mypage.ui

import com.doki.productpackage.reservation.application.dto.ReservationSummaryResponse
import com.doki.review.application.dto.PackageReviewResponse
import com.doki.user.application.dto.GetUserLoginInfoResponse
import com.doki.user.user.application.dto.GetUserLoginInfoResponse
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.Content
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/doki/mypage/ui/MyPageController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import com.doki.productpackage.reservation.application.ReservationQueryService
import com.doki.productpackage.reservation.application.dto.ReservationSummaryResponse
import com.doki.review.application.PackageReviewQueryService
import com.doki.review.application.dto.PackageReviewResponse
import com.doki.user.application.UserService
import com.doki.user.application.dto.GetUserLoginInfoResponse
import com.doki.user.user.application.UserService
import com.doki.user.user.application.dto.GetUserLoginInfoResponse
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
Expand Down
12 changes: 6 additions & 6 deletions src/main/kotlin/com/doki/user/auth/application/AuthService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import com.doki.user.auth.domain.vo.AuthProvider
import com.doki.global.exceptions.CustomException
import com.doki.global.exceptions.auth.AuthExceptionType
import com.doki.global.security.JwtProvider
import com.doki.user.domain.User
import com.doki.user.user.domain.User
import com.doki.user.auth.domain.UserOAuth
import com.doki.user.infrastructure.UserOAuthRepository
import com.doki.user.infrastructure.UserRepository
import com.doki.user.user.domain.UserRepository
import com.doki.user.user.infrastructure.UserOAuthRepository
import jakarta.servlet.http.HttpServletResponse
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand Down Expand Up @@ -46,10 +46,10 @@ class AuthService(
}

val user = userRepository.findById(userId)
.orElseThrow { CustomException(AuthExceptionType.USER_NOT_FOUND) }
?: throw CustomException(AuthExceptionType.USER_NOT_FOUND)

// 마지막 로그인 시각 업데이트
user.updateLasLoginTime()
user.updateLastLoginTime()

return jwtProvider.generateToken(user.id, user.role)
}
Expand All @@ -67,7 +67,7 @@ class AuthService(

val userId = jwtProvider.getSubjectAsUserId(refreshToken)
val user = userRepository.findById(userId)
.orElseThrow { CustomException(AuthExceptionType.USER_NOT_FOUND) }
?: throw CustomException(AuthExceptionType.USER_NOT_FOUND)

// accessToken, refreshToken 재발급
val tokenResponse = jwtProvider.generateToken(user.id, user.role)
Expand Down
21 changes: 0 additions & 21 deletions src/main/kotlin/com/doki/user/dto/GetUserLoginInfoResponse.kt

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.doki.user.application
package com.doki.user.user.application

import com.doki.global.exceptions.CustomException
import com.doki.global.exceptions.auth.AuthExceptionType
import com.doki.user.application.dto.GetUserLoginInfoResponse
import com.doki.user.domain.User
import com.doki.user.infrastructure.UserRepository
import com.doki.user.user.application.dto.GetUserLoginInfoResponse
import com.doki.user.user.domain.User
import com.doki.user.user.domain.UserRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -20,7 +20,7 @@ class UserService(

private fun findUser(userId: Long): User =
userRepository.findById(userId)
.orElseThrow { CustomException(AuthExceptionType.USER_NOT_FOUND) }
?: throw CustomException(AuthExceptionType.USER_NOT_FOUND)

@Transactional
fun updateNickname(userId: Long, nickname: String): GetUserLoginInfoResponse {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.doki.user.user.application

import com.doki.user.user.application.dto.CreateUserSurveyCommand
import com.doki.user.user.domain.UserRepository
import com.doki.user.user.domain.UserSurvey
import com.doki.user.user.domain.dto.UserSurveyCreatedResponse
import com.doki.user.user.domain.dto.UserSurveyResponse
import com.doki.user.user.domain.UserSurveyRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Transactional
@Service
class UserSurveyCommandService(
private val userRepository: UserRepository,
private val userSurveyRepository: UserSurveyRepository,
) {

fun saveUserSurvey(userId: Long, command: CreateUserSurveyCommand): UserSurveyCreatedResponse {
val user = userRepository.findById(userId)
?: throw IllegalArgumentException("User with id $userId does not exist.")

if (userSurveyRepository.existsByUserId(userId)) {
throw IllegalArgumentException("UserSurvey already exists.")
}

val survey = UserSurvey.of(
user = user,
concept = command.concept,
idolName = command.idolName,
visitStartDate = command.visitStartDate,
visitEndDate = command.visitEndDate,
places = command.places
)

val saved = userSurveyRepository.save(survey)
return UserSurveyCreatedResponse(saved.id)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.doki.user.user.application

import com.doki.user.user.domain.UserSurveyRepository
import com.doki.user.user.domain.dto.UserSurveyResponse
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Transactional(readOnly = true)
@Service
class UserSurveyQueryService(
private val userSurveyRepository: UserSurveyRepository
) {

fun findUserSurvey(userId: Long): UserSurveyResponse {
val survey = userSurveyRepository.findByUserId(userId)
?: throw IllegalArgumentException("UserSurvey does not exist.")

return UserSurveyResponse.from(survey)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.doki.user.user.application.dto

import com.doki.user.user.domain.vo.ConceptType
import java.time.LocalDate

data class CreateUserSurveyCommand (
val concept: ConceptType,
val idolName: String,
val visitStartDate: LocalDate,
val visitEndDate: LocalDate,
val places: List<String>
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.doki.user.application.dto
package com.doki.user.user.application.dto

import com.doki.user.domain.User
import com.doki.user.user.domain.User
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDateTime

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.doki.user.domain
package com.doki.user.user.domain

import com.doki.user.domain.vo.Nickname
import com.doki.user.domain.vo.UserRole
import com.doki.user.user.domain.vo.Nickname
import com.doki.user.user.domain.vo.UserRole
import jakarta.persistence.Column
import jakarta.persistence.Embedded
import jakarta.persistence.Entity
Expand Down Expand Up @@ -34,7 +34,7 @@ class User(
var lastLoginAt: LocalDateTime? = null
) {
// 마지막 로그인 시점 업데이트
fun updateLasLoginTime(now: LocalDateTime = LocalDateTime.now()) {
fun updateLastLoginTime(now : LocalDateTime = LocalDateTime.now()) {
this.lastLoginAt = now
}

Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/com/doki/user/user/domain/UserRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.doki.user.user.domain

interface UserRepository {

fun findById(userId: Long): User?

fun findByEmail(email: String): User?

fun save(user: User): User
}
70 changes: 70 additions & 0 deletions src/main/kotlin/com/doki/user/user/domain/UserSurvey.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.doki.user.user.domain

import com.doki.global.BaseEntity
import com.doki.user.user.domain.vo.ConceptType
import jakarta.persistence.*
import java.time.LocalDate

@Entity
@Table(name = "user_survey")
class UserSurvey(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,

@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false, unique = true)
val user: User,

@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 30)
val concept: ConceptType,

@Column(name = "idol_name", nullable = false)
val idolName: String,

@Column(name = "visit_start_date", nullable = false)
val visitStartDate: LocalDate,

@Column(name = "visit_end_date", nullable = false)
val visitEndDate: LocalDate,

@ElementCollection
@CollectionTable(
name = "user_survey_places",
joinColumns = [JoinColumn(name = "user_survey_id")]
)
@Column(name = "place", nullable = false, length = 100)
val places: List<String>
Comment on lines +32 to +38
Copy link
Contributor

Choose a reason for hiding this comment

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

사전 위치 기반으로 데이터를 제공해준다면, 해당 정보가 필요할 거 같은데 조회할 때 복잡한 요구사항은 없을 것 같아서 지금 구조도 괜찮아 보입니다! 나중에 위치 데이터를 복잡하게 다룬다면 그때 개선하는 걸로 하시죠!!

) : BaseEntity() {

init {
require(places.isNotEmpty()) { "place must not be empty" }
require(!visitEndDate.isBefore(visitStartDate)) { "visitEndDate must be on or after visitStartDate" }
}

companion object {
private fun normalizePlaces(places: List<String>): List<String> =
places.map { it.trim() }
.filter { it.isNotBlank() }
.distinct()

fun of(
user: User,
concept: ConceptType,
idolName: String,
visitStartDate: LocalDate,
visitEndDate: LocalDate,
places: List<String>
): UserSurvey {
return UserSurvey(
user = user,
concept = concept,
idolName = idolName.trim(),
visitStartDate = visitStartDate,
visitEndDate = visitEndDate,
places = normalizePlaces(places)
)
}
}
}
10 changes: 10 additions & 0 deletions src/main/kotlin/com/doki/user/user/domain/UserSurveyRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.doki.user.user.domain

interface UserSurveyRepository {

fun findByUserId(userId: Long): UserSurvey?

fun existsByUserId(userId: Long): Boolean

fun save(userSurvey: UserSurvey): UserSurvey
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.doki.user.user.domain.dto

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

data class UserSurveyCreatedResponse(
@field:Schema(description = "생성된 설문조사 ID", example = "1")
val id: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.doki.user.user.domain.dto

import com.doki.user.user.domain.UserSurvey
import com.doki.user.user.domain.vo.ConceptType
import io.swagger.v3.oas.annotations.media.Schema
import java.time.LocalDate

data class UserSurveyResponse(
@field:Schema(description = "설문조사 ID", example = "1")
val id: Long,

@field:Schema(description = "선호 컨셉", example = "GIRL_CRUSH")
val concept: ConceptType,

@field:Schema(description = "선호 아이돌 또는 그룹", example = "에스파")
val idolName: String,

@field:Schema(description = "여행 시작일", example = "2025-07-20")
val visitStartDate: LocalDate,

@field:Schema(description = "여행 종료일", example = "2025-07-22")
val visitEndDate: LocalDate,

@field:Schema(description = "여행 장소", example = "['용산구', '마포구']")
val places: List<String>
) {
companion object {
fun from(userSurvey: UserSurvey): UserSurveyResponse {
return UserSurveyResponse(
id = userSurvey.id,
concept = userSurvey.concept,
idolName = userSurvey.idolName,
visitStartDate = userSurvey.visitStartDate,
visitEndDate = userSurvey.visitEndDate,
places = userSurvey.places
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.doki.user.domain.exception
package com.doki.user.user.domain.exception

import com.doki.global.exceptions.CustomExceptionType

Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/com/doki/user/user/domain/vo/ConceptType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.doki.user.user.domain.vo

enum class ConceptType {
GIRL_CRUSH,
LOVELY_FRESH,
ELEGANT_GLAM,
DREAMY,
HIGHTEEN,
ETC
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.doki.user.domain.vo
package com.doki.user.user.domain.vo

import com.doki.global.exceptions.CustomException
import com.doki.user.domain.exception.UserExceptionType
import com.doki.user.user.domain.exception.UserExceptionType
import jakarta.persistence.Column
import jakarta.persistence.Embeddable

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.doki.user.domain.vo
package com.doki.user.user.domain.vo

enum class UserRole {
USER // 회원
Expand Down
Loading