Skip to content
This repository was archived by the owner on Jul 7, 2025. It is now read-only.
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 @@ -26,5 +26,7 @@ interface SpaceManagementPort {

fun deleteBy(space: Space)

fun deleteAll()

fun countByUserId(userId: DomainId): Long
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class SpaceCommandService(
override fun create(command: CreateSpaceUsecase.Command) {
val userId = DomainId(command.userId)
Space.create(
id = DomainId.generate(),
userId = userId,
name = command.spaceName,
templateType = command.templateType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ interface UserManagementPort {

@Throws(UserException.UserNotFoundException::class)
fun getUserNotNull(userId: DomainId): User

fun findById(userId: DomainId): User?
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class SocialLoginService(
userManagementPort.getUser(userAuth.userId)?.let { user ->
val accessToken = userTokenConvertPort.generateAccessToken(user)
val refreshToken = userTokenConvertPort.generateRefreshToken(user)
userTokenManagementPort.saveUserToken(UserToken(token = refreshToken, userId = user.id))
userTokenManagementPort.saveUserToken(UserToken.create(token = refreshToken, userId = user.id))
SocialLoginUsecase.Success(accessToken, refreshToken, user.isProcessedOnboarding())
} ?: run {
throw DefaultException.InvalidStateException("사용자 인증정보만 존재합니다. - ${userAuth.userId}")
Expand All @@ -39,7 +39,7 @@ class SocialLoginService(
profileImage = authInfo.profileImage,
email = authInfo.email,
)
userTokenManagementPort.saveUserToken(UserToken(token = registerToken))
userTokenManagementPort.saveUserToken(UserToken.create(token = registerToken))
SocialLoginUsecase.NonRegistered(registerToken)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class UserCommandService(
email = userClaims.email,
)
val userAuth =
UserAuth(
UserAuth.create(
userId = registerUser.id,
socialId = userClaims.socialId,
socialLoginProvider = userClaims.socialLoginProvider,
Expand All @@ -60,7 +60,7 @@ class UserCommandService(
val accessToken = userTokenConvertPort.generateAccessToken(registerUser)
val refreshToken = userTokenConvertPort.generateRefreshToken(registerUser)

userTokenManagementPort.saveUserToken(UserToken(token = refreshToken, userId = registerUser.id))
userTokenManagementPort.saveUserToken(UserToken.create(token = refreshToken, userId = registerUser.id))

return RegisterUserUsecase.Response(accessToken, refreshToken)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class UserTokenCommandService(

val accessToken = userTokenConvertPort.generateAccessToken(user)
val refreshToken = userTokenConvertPort.generateRefreshToken(user)
userTokenManagementPort.saveUserToken(UserToken(userId = user.id, token = refreshToken))
userTokenManagementPort.saveUserToken(UserToken.create(userId = user.id, token = refreshToken))
return ReissueTokenUsecase.Response(
accessToken = accessToken,
refreshToken = refreshToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class SocialLoginServiceTest :
var command = SocialLoginUsecase.Command(SocialLoginProvider.KAKAO.name, "registered")
val authInfo = AuthInfo(SocialLoginProvider.KAKAO, "socialId", "name", "email", "profileImage")
val getUserAuth =
UserAuth(
UserAuth.create(
userId = DomainId.generate(),
socialId = "socialId",
socialLoginProvider = SocialLoginProvider.KAKAO,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class LetterMockManager(
),
spaceId = DomainId(spaceId),
receiveDate = LocalDate.now(),

)
spaceLetterManagementPort.save(
spaceLetter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class SpaceMockManager(
): Space {
val space =
Space.create(
id = DomainId.generate(),
userId = DomainId(userId),
name = "test",
templateType = 0,
Expand All @@ -28,4 +29,8 @@ class SpaceMockManager(
}
}
}

fun clear() {
spaceManagementPort.deleteAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.asap.domain.user.entity.UserAuth
import com.asap.domain.user.enums.SocialLoginProvider
import com.asap.domain.user.vo.UserPermission
import java.time.LocalDate
import java.time.LocalDateTime

class UserMockManager(
private val userManagementPort: UserManagementPort,
Expand All @@ -33,7 +34,9 @@ class UserMockManager(
permission = UserPermission(true, true, true),
birthday = LocalDate.now(),
email = "email",
onboardingAt = null,
onboardingAt = LocalDateTime.now(),
createdAt = LocalDateTime.now(),
updatedAt = LocalDateTime.now()
),
)

Expand All @@ -43,7 +46,7 @@ class UserMockManager(
provider: String = "KAKAO",
) {
userAuthManagementPort.saveUserAuth(
UserAuth(
UserAuth.create(
userId = DomainId(userId),
socialId = socialId,
socialLoginProvider = SocialLoginProvider.parse(provider),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,7 @@ class LetterApiIntegrationTest : IntegrationSupporter() {
contentType = MediaType.APPLICATION_JSON
header("Authorization", "Bearer $accessToken")
}

// then
response.andExpect {
status { isOk() }
Expand All @@ -893,6 +894,32 @@ class LetterApiIntegrationTest : IntegrationSupporter() {
}
}
}

@Test
fun getAllLetterCount_with_space_count(){
// given
val senderId = userMockManager.settingUser()
val receiverId = userMockManager.settingUser()
val accessToken = jwtMockManager.generateAccessToken(receiverId)
val space = spaceMockManager.settingSpace(receiverId)

// when
val response =
mockMvc.get("/api/v1/letters/count") {
contentType = MediaType.APPLICATION_JSON
header("Authorization", "Bearer $accessToken")
}

// then
response.andExpect {
status { isOk() }
jsonPath("$.spaceCount") {
exists()
isNumber()
value(1)
}
}
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.asap.bootstrap.integration.user

import com.asap.application.user.exception.UserException
import com.asap.application.user.port.out.UserManagementPort
import com.asap.bootstrap.IntegrationSupporter
import com.asap.bootstrap.web.user.dto.LogoutRequest
import com.asap.bootstrap.web.user.dto.RegisterUserRequest
import com.asap.bootstrap.web.user.dto.UpdateBirthdayRequest
import com.asap.domain.common.DomainId
import io.kotest.matchers.comparables.shouldBeGreaterThan
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.http.MediaType
Expand All @@ -14,7 +17,9 @@ import org.springframework.test.web.servlet.post
import org.springframework.test.web.servlet.put
import java.time.LocalDate

class UserApiIntegrationTest : IntegrationSupporter() {
class UserApiIntegrationTest(
private val userManagementPort: UserManagementPort
) : IntegrationSupporter() {
@Test
fun registerUserSuccessTest() {
// given
Expand Down Expand Up @@ -260,4 +265,23 @@ class UserApiIntegrationTest : IntegrationSupporter() {
status { isOk() }
}
}

@Test
fun deleteUser_user_updated_at_updated() {
// given
val userId = userMockManager.settingUser()
userMockManager.settingUserAuth(userId = userId)
val accessToken = jwtMockManager.generateAccessToken(userId)

// when
mockMvc.delete("/api/v1/users") {
contentType = MediaType.APPLICATION_JSON
header("Authorization", "Bearer $accessToken")
}


// then
val user = userManagementPort.getUserNotNull(DomainId(userId))
user.updatedAt shouldBeGreaterThan user.createdAt
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.asap.domain.common

import io.github.oshai.kotlinlogging.KotlinLogging
import java.time.LocalDateTime

abstract class Aggregate<T : Aggregate<T>>(
val id: DomainId,
) {
id: DomainId,
createdAt: LocalDateTime,
updatedAt: LocalDateTime,
): BaseEntity(id, createdAt, updatedAt) {
private val logger = KotlinLogging.logger {}
private val events: MutableList<DomainEvent<T>> = mutableListOf()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package com.asap.domain.common

import java.time.LocalDateTime

abstract class BaseEntity(
val id: DomainId = DomainId.generate()
val id: DomainId = DomainId.generate(),
val createdAt: LocalDateTime,
var updatedAt: LocalDateTime,
) {

protected fun updateTime() {
updatedAt = LocalDateTime.now()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ class DraftLetter(
var receiverName: String,
val ownerId: DomainId,
var images: List<String>,
var lastUpdated: LocalDateTime = LocalDateTime.now(),
) : Aggregate<DraftLetter>(id) {
var lastUpdated: LocalDateTime,
createdAt: LocalDateTime,
) : Aggregate<DraftLetter>(id, createdAt, lastUpdated) {
companion object {
fun default(ownerId: DomainId) =
DraftLetter(
Expand All @@ -20,6 +21,8 @@ class DraftLetter(
content = "",
receiverName = "",
images = emptyList(),
lastUpdated = LocalDateTime.now(),
createdAt = LocalDateTime.now(),
)
}

Expand All @@ -32,5 +35,6 @@ class DraftLetter(
this.receiverName = receiverName
this.images = images
this.lastUpdated = LocalDateTime.now()
updateTime()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ class IndependentLetter(
val receiveDate: LocalDate,
val movedAt: LocalDateTime,
var isOpened: Boolean,
) : Aggregate<IndependentLetter>(id) {
createdAt: LocalDateTime,
updatedAt: LocalDateTime,
) : Aggregate<IndependentLetter>(id, createdAt, updatedAt) {
companion object {
fun createBySpaceLetter(
spaceLetter: SpaceLetter,
Expand All @@ -31,6 +33,8 @@ class IndependentLetter(
receiveDate = spaceLetter.receiveDate,
movedAt = LocalDateTime.now(),
isOpened = false,
createdAt = spaceLetter.createdAt,
updatedAt = LocalDateTime.now(),
)

fun create(
Expand All @@ -42,6 +46,8 @@ class IndependentLetter(
movedAt: LocalDateTime = LocalDateTime.now(),
isOpened: Boolean = false,
draftId: DomainId? = null,
createdAt: LocalDateTime = LocalDateTime.now(),
updatedAt: LocalDateTime = LocalDateTime.now(),
): IndependentLetter =
IndependentLetter(
id = id,
Expand All @@ -51,6 +57,8 @@ class IndependentLetter(
receiveDate = receiveDate,
movedAt = movedAt,
isOpened = isOpened,
createdAt = createdAt,
updatedAt = updatedAt,
).also {
it.registerEvent(IndependentLetterEvent.IndependentLetterCreatedEvent(it, draftId?.value))
}
Expand All @@ -60,6 +68,7 @@ class IndependentLetter(

fun read() {
isOpened = true
updateTime()
}

fun update(
Expand All @@ -72,11 +81,13 @@ class IndependentLetter(
this.content.updateContent(content)
this.content.updateImages(images.toMutableList())
this.content.updateTemplateType(templateType)
updateTime()
}

fun delete() {
this.content.delete()
this.sender.delete()
updateTime()
}

fun getOwnerId(): DomainId = receiver.receiverId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,24 @@ class LetterLog(
val loggedAt: LocalDateTime,
val logType: LetterLogType,
val content: String,
) : BaseEntity(id) {
createdAt: LocalDateTime,
updatedAt: LocalDateTime,
) : BaseEntity(id, createdAt, updatedAt) {
companion object {
fun create(
targetLetterId: DomainId,
logType: LetterLogType,
content: String,
createdAt: LocalDateTime = LocalDateTime.now(),
updatedAt: LocalDateTime = LocalDateTime.now(),
): LetterLog =
LetterLog(
targetLetterId = targetLetterId,
loggedAt = LocalDateTime.now(),
logType = logType,
content = content,
createdAt = createdAt,
updatedAt = updatedAt,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ class ReceiveDraftLetter(
var senderName: String,
val ownerId: DomainId,
var images: List<String>,
var lastUpdated: LocalDateTime = LocalDateTime.now(),
var lastUpdated: LocalDateTime,
val type: ReceiveDraftLetterType, // TODO: 상속 구조를 통한 타입 구분 생각해보기
) : BaseEntity(id) {
createdAt: LocalDateTime,
) : BaseEntity(id, createdAt, lastUpdated) {
companion object {
fun default(ownerId: DomainId) =
ReceiveDraftLetter(
Expand All @@ -22,6 +23,8 @@ class ReceiveDraftLetter(
senderName = "",
images = emptyList(),
type = ReceiveDraftLetterType.PHYSICAL,
lastUpdated = LocalDateTime.now(),
createdAt = LocalDateTime.now(),
)
}

Expand All @@ -34,6 +37,7 @@ class ReceiveDraftLetter(
this.senderName = senderName
this.images = images
this.lastUpdated = LocalDateTime.now()
updateTime()
}
}

Expand Down
Loading
Loading