From 70c56f81921d78faa138b576904fbf2c30fa51e7 Mon Sep 17 00:00:00 2001 From: xueque Date: Fri, 16 May 2025 23:03:47 +0800 Subject: [PATCH] demo --- src/main/kotlin/Program.kt | 31 ++++--- .../ddd/application/DefaultUserService.kt | 10 ++- .../kotlin/ddd/application/UserService.kt | 4 +- .../ddd/application/dto/ChangeUsername.kt | 3 - .../kotlin/ddd/controller/UserController.kt | 4 +- src/main/kotlin/ddd/domain/User.kt | 19 +++-- .../{port => adapter}/VerificationService.kt | 14 +-- .../domain/entity/InternalUserRankPolicy.kt | 2 - .../EmailVerificationValidation.kt | 2 +- .../ExistsUsernameValidation.kt | 3 + .../changeUsername/UsernameChangeContext.kt | 2 +- .../ddd/domain/valueobject/UserStatusEnum.kt | 6 +- .../repository/MemoryUserRepository.kt | 2 +- .../verification/EmailVerificationService.kt | 4 +- .../kotlin/mvc/controllers/UserController.kt | 4 +- src/main/kotlin/mvc/dao/UserRepository.kt | 8 +- src/main/kotlin/mvc/entities/User.kt | 9 +- src/main/kotlin/mvc/entities/UserRankEnum.kt | 5 ++ .../kotlin/mvc/entities/UserStatusEnum.kt | 5 ++ .../kotlin/mvc/services/UserComplexService.kt | 85 +++++++++++++++++-- src/main/kotlin/mvc/services/UserService.kt | 3 +- .../kotlin/mvc/services/UserSimpleService.kt | 12 +-- .../mvc/services/VerificationService.kt | 7 ++ src/main/kotlin/shared/AggregateRoot.kt | 5 ++ .../kotlin/shared/dto/ChangeUsernameDto.kt | 3 + 25 files changed, 186 insertions(+), 66 deletions(-) delete mode 100644 src/main/kotlin/ddd/application/dto/ChangeUsername.kt rename src/main/kotlin/ddd/domain/{port => adapter}/VerificationService.kt (84%) create mode 100644 src/main/kotlin/mvc/entities/UserRankEnum.kt create mode 100644 src/main/kotlin/mvc/entities/UserStatusEnum.kt create mode 100644 src/main/kotlin/mvc/services/VerificationService.kt create mode 100644 src/main/kotlin/shared/AggregateRoot.kt create mode 100644 src/main/kotlin/shared/dto/ChangeUsernameDto.kt diff --git a/src/main/kotlin/Program.kt b/src/main/kotlin/Program.kt index 7c0bcf2..2ac40e7 100644 --- a/src/main/kotlin/Program.kt +++ b/src/main/kotlin/Program.kt @@ -1,22 +1,19 @@ import ddd.application.DefaultUserService -import ddd.application.dto.ChangeUsername +import shared.dto.ChangeUsernameDto import ddd.domain.service.ExistsUserDomainService import ddd.infrastructure.repository.MemoryUserRepository import ddd.infrastructure.verification.EmailVerificationService -import mvc.controllers.UserController -import mvc.dao.UserRepository -import mvc.entities.User -import mvc.services.UserSimpleService -//fun main() { -// val controller = UserController(UserSimpleService(UserRepository())) -// val changeUsername = controller.changeUsername(User(1L, "nian", "3")) -// println(changeUsername) -//} +fun mvc() { + val controller = mvc.controllers.UserController(mvc.services.UserComplexService(mvc.dao.UserRepository())) + val changeUsername1 = controller.changeUsername(ChangeUsernameDto(1L, "nian", "abc","1234")) + println(changeUsername1) + val changeUsername2 = controller.changeUsername(ChangeUsernameDto(1L, "nian", "def","1234")) + println(changeUsername2) +} -fun main() { +fun ddd() { val memoryUserRepository = MemoryUserRepository() - val userController = ddd.controller.UserController( DefaultUserService( memoryUserRepository, @@ -24,6 +21,12 @@ fun main() { ExistsUserDomainService(memoryUserRepository) ) ) - val changeUsername = userController.changeUsername(ChangeUsername(1L, "nian", "po","")) - println(changeUsername) + val changeUsername1 = userController.changeUsername(ChangeUsernameDto(1L, "nian", "po","1234")) + println(changeUsername1) + val changeUsername2 = userController.changeUsername(ChangeUsernameDto(1L, "nian", "po1","1234")) + println(changeUsername2) +} + +fun main(){ + mvc() } \ No newline at end of file diff --git a/src/main/kotlin/ddd/application/DefaultUserService.kt b/src/main/kotlin/ddd/application/DefaultUserService.kt index 7655c4a..78b7b28 100644 --- a/src/main/kotlin/ddd/application/DefaultUserService.kt +++ b/src/main/kotlin/ddd/application/DefaultUserService.kt @@ -1,8 +1,8 @@ package ddd.application -import ddd.application.dto.ChangeUsername +import shared.dto.ChangeUsernameDto import ddd.domain.User -import ddd.domain.port.VerificationService +import ddd.domain.adapter.VerificationService import ddd.domain.repository.UserRepository import ddd.domain.service.ExistsUserDomainService import ddd.domain.valueobject.Username @@ -13,14 +13,16 @@ class DefaultUserService( val verificationService: VerificationService, val existsUserDomainService: ExistsUserDomainService, ) : UserService { - override fun changeUsername(userDto: ChangeUsername): User { - val user = userRepository.findById(userDto.id) ?: throw NotFoundException("User with id ${userDto.id} not found") + override fun changeUsername(userDto: ChangeUsernameDto): User { + val user = userRepository.findById(userDto.id) ?: throw NotFoundException("用户${userDto.id}不存在") + user.changeUsername( Username(userDto.firstName, userDto.lastName), userDto.verificationCode, verificationService, existsUserDomainService ) + userRepository.save(user) return user } diff --git a/src/main/kotlin/ddd/application/UserService.kt b/src/main/kotlin/ddd/application/UserService.kt index cee7c1a..1de47cb 100644 --- a/src/main/kotlin/ddd/application/UserService.kt +++ b/src/main/kotlin/ddd/application/UserService.kt @@ -1,8 +1,8 @@ package ddd.application -import ddd.application.dto.ChangeUsername +import shared.dto.ChangeUsernameDto import ddd.domain.User interface UserService { - fun changeUsername(userDto: ChangeUsername): User + fun changeUsername(userDto: ChangeUsernameDto): User } \ No newline at end of file diff --git a/src/main/kotlin/ddd/application/dto/ChangeUsername.kt b/src/main/kotlin/ddd/application/dto/ChangeUsername.kt deleted file mode 100644 index 9bf215d..0000000 --- a/src/main/kotlin/ddd/application/dto/ChangeUsername.kt +++ /dev/null @@ -1,3 +0,0 @@ -package ddd.application.dto - -data class ChangeUsername(val id: Long ,val firstName: String, val lastName: String, val verificationCode: String) \ No newline at end of file diff --git a/src/main/kotlin/ddd/controller/UserController.kt b/src/main/kotlin/ddd/controller/UserController.kt index 9d851ba..e150142 100644 --- a/src/main/kotlin/ddd/controller/UserController.kt +++ b/src/main/kotlin/ddd/controller/UserController.kt @@ -1,9 +1,9 @@ package ddd.controller import ddd.application.UserService -import ddd.application.dto.ChangeUsername +import shared.dto.ChangeUsernameDto class UserController(val service: UserService) { - fun changeUsername(changeUsername: ChangeUsername) = service.changeUsername(changeUsername) + fun changeUsername(changeUsername: ChangeUsernameDto) = service.changeUsername(changeUsername) } \ No newline at end of file diff --git a/src/main/kotlin/ddd/domain/User.kt b/src/main/kotlin/ddd/domain/User.kt index d9c6bf5..083b939 100644 --- a/src/main/kotlin/ddd/domain/User.kt +++ b/src/main/kotlin/ddd/domain/User.kt @@ -2,27 +2,28 @@ package ddd.domain import ddd.domain.entity.UserRank import ddd.domain.valueobject.UserId -import ddd.domain.valueobject.UserStatusEnum import ddd.domain.valueobject.Username -import ddd.domain.port.VerificationService +import ddd.domain.adapter.VerificationService import ddd.domain.service.ExistsUserDomainService import ddd.domain.validation.changeUsername.EmailVerificationValidation import ddd.domain.validation.changeUsername.ExistsUsernameValidation import ddd.domain.validation.changeUsername.RankPolicyValidation import ddd.domain.validation.changeUsername.TimeIntervalValidation import ddd.domain.validation.changeUsername.UsernameChangeContext +import ddd.domain.valueobject.UserStatusEnum +import shared.AggregateRoot import shared.exceptions.ChangeUsernameException import shared.validation.ValidationChain import java.time.Clock import java.time.LocalDateTime class User( - val id: UserId, + override val id: UserId, var username: Username, var status: UserStatusEnum, var lastUsernameChange: LocalDateTime?, var rank: UserRank, -) { +) : AggregateRoot() { // 领域方法:修改用户名(入口点) fun changeUsername( newUsername: Username, @@ -32,7 +33,7 @@ class User( clock: Clock = Clock.systemDefaultZone() ) { validateState() - validateChangeUsername(newUsername, verificationCode, verificationService, existsUserService, clock) + validateUsername(newUsername, verificationCode, verificationService, existsUserService, clock) executeUsernameChange(newUsername, clock) } @@ -42,7 +43,7 @@ class User( } } - private fun validateChangeUsername( + private fun validateUsername( newUsername: Username, verificationCode: String, verificationService: VerificationService, @@ -60,8 +61,8 @@ class User( // 组合验证规则(责任链模式) ValidationChain() - .add(EmailVerificationValidation()) .add(RankPolicyValidation()) + .add(EmailVerificationValidation()) .add(TimeIntervalValidation()) .add(ExistsUsernameValidation()) .validate(context) @@ -71,4 +72,8 @@ class User( username = newUsername lastUsernameChange = LocalDateTime.now(clock) } + + override fun toString(): String { + return "User(id=$id, username=$username, status=$status, lastUsernameChange=$lastUsernameChange, rank=${rank.value})" + } } \ No newline at end of file diff --git a/src/main/kotlin/ddd/domain/port/VerificationService.kt b/src/main/kotlin/ddd/domain/adapter/VerificationService.kt similarity index 84% rename from src/main/kotlin/ddd/domain/port/VerificationService.kt rename to src/main/kotlin/ddd/domain/adapter/VerificationService.kt index 918525b..2059e67 100644 --- a/src/main/kotlin/ddd/domain/port/VerificationService.kt +++ b/src/main/kotlin/ddd/domain/adapter/VerificationService.kt @@ -1,8 +1,8 @@ -package ddd.domain.port - -import ddd.domain.valueobject.UserId - -interface VerificationService { - fun isVerified(userId: UserId, code: String): Boolean - fun sendVerificationCode(userId: UserId) +package ddd.domain.adapter + +import ddd.domain.valueobject.UserId + +interface VerificationService { + fun isVerified(userId: UserId, code: String): Boolean + fun sendVerificationCode(userId: UserId) } \ No newline at end of file diff --git a/src/main/kotlin/ddd/domain/entity/InternalUserRankPolicy.kt b/src/main/kotlin/ddd/domain/entity/InternalUserRankPolicy.kt index 9c68fc3..cb9c3a8 100644 --- a/src/main/kotlin/ddd/domain/entity/InternalUserRankPolicy.kt +++ b/src/main/kotlin/ddd/domain/entity/InternalUserRankPolicy.kt @@ -4,8 +4,6 @@ import ddd.domain.User class InternalUserRankPolicy : UserRankPolicy { override fun canChangeUsername(user: User) = true - override fun requiresEmailVerification() = false - override fun getMaxChangeIntervalDays() = 0 } \ No newline at end of file diff --git a/src/main/kotlin/ddd/domain/validation/changeUsername/EmailVerificationValidation.kt b/src/main/kotlin/ddd/domain/validation/changeUsername/EmailVerificationValidation.kt index 016e46d..bf92341 100644 --- a/src/main/kotlin/ddd/domain/validation/changeUsername/EmailVerificationValidation.kt +++ b/src/main/kotlin/ddd/domain/validation/changeUsername/EmailVerificationValidation.kt @@ -7,7 +7,7 @@ class EmailVerificationValidation : AbstractValidationHandler() { override fun validate(context: UsernameChangeContext) { + check(context.user.username != context.newUsername){ + throw ChangeUsernameException("不能修改为当前用户名") + } require(!context.existsUserService.existsByUsername(context.newUsername)) { throw ChangeUsernameException("用户名已存在") } diff --git a/src/main/kotlin/ddd/domain/validation/changeUsername/UsernameChangeContext.kt b/src/main/kotlin/ddd/domain/validation/changeUsername/UsernameChangeContext.kt index c06a3ba..c8e8ae2 100644 --- a/src/main/kotlin/ddd/domain/validation/changeUsername/UsernameChangeContext.kt +++ b/src/main/kotlin/ddd/domain/validation/changeUsername/UsernameChangeContext.kt @@ -1,7 +1,7 @@ package ddd.domain.validation.changeUsername import ddd.domain.User -import ddd.domain.port.VerificationService +import ddd.domain.adapter.VerificationService import ddd.domain.service.ExistsUserDomainService import ddd.domain.valueobject.Username import java.time.Clock diff --git a/src/main/kotlin/ddd/domain/valueobject/UserStatusEnum.kt b/src/main/kotlin/ddd/domain/valueobject/UserStatusEnum.kt index 396235c..194d1ff 100644 --- a/src/main/kotlin/ddd/domain/valueobject/UserStatusEnum.kt +++ b/src/main/kotlin/ddd/domain/valueobject/UserStatusEnum.kt @@ -1,5 +1,5 @@ -package ddd.domain.valueobject; +package ddd.domain.valueobject -public enum UserStatusEnum { +enum class UserStatusEnum { ACTIVE,DEACTIVATED,BANNED -} +} \ No newline at end of file diff --git a/src/main/kotlin/ddd/infrastructure/repository/MemoryUserRepository.kt b/src/main/kotlin/ddd/infrastructure/repository/MemoryUserRepository.kt index 5a9dc6d..6f157d5 100644 --- a/src/main/kotlin/ddd/infrastructure/repository/MemoryUserRepository.kt +++ b/src/main/kotlin/ddd/infrastructure/repository/MemoryUserRepository.kt @@ -17,7 +17,7 @@ class MemoryUserRepository : UserRepository { Username("nian", "chen"), UserStatusEnum.ACTIVE, null, - UserRank.RegularUserRank() + UserRank.VipUserRank() ) ) } diff --git a/src/main/kotlin/ddd/infrastructure/verification/EmailVerificationService.kt b/src/main/kotlin/ddd/infrastructure/verification/EmailVerificationService.kt index 49fcc3e..1a5da94 100644 --- a/src/main/kotlin/ddd/infrastructure/verification/EmailVerificationService.kt +++ b/src/main/kotlin/ddd/infrastructure/verification/EmailVerificationService.kt @@ -1,9 +1,9 @@ package ddd.infrastructure.verification -import ddd.domain.port.VerificationService +import ddd.domain.adapter.VerificationService import ddd.domain.valueobject.UserId class EmailVerificationService : VerificationService { - override fun isVerified(userId: UserId, code: String) = true + override fun isVerified(userId: UserId, code: String) = code == "1234" override fun sendVerificationCode(userId: UserId) = println("sending verification email") } \ No newline at end of file diff --git a/src/main/kotlin/mvc/controllers/UserController.kt b/src/main/kotlin/mvc/controllers/UserController.kt index ac5f288..0f990fa 100644 --- a/src/main/kotlin/mvc/controllers/UserController.kt +++ b/src/main/kotlin/mvc/controllers/UserController.kt @@ -1,8 +1,8 @@ package mvc.controllers -import mvc.entities.User import mvc.services.UserService +import shared.dto.ChangeUsernameDto class UserController(val service: UserService) { - fun changeUsername(user: User) = service.changeUsername(user) + fun changeUsername(userDto: ChangeUsernameDto) = service.changeUsername(userDto) } \ No newline at end of file diff --git a/src/main/kotlin/mvc/dao/UserRepository.kt b/src/main/kotlin/mvc/dao/UserRepository.kt index 5ec0424..1778141 100644 --- a/src/main/kotlin/mvc/dao/UserRepository.kt +++ b/src/main/kotlin/mvc/dao/UserRepository.kt @@ -1,12 +1,14 @@ package mvc.dao import mvc.entities.User +import mvc.entities.UserRankEnum +import mvc.entities.UserStatusEnum class UserRepository { val users = mutableMapOf() init { - add(User(1L, "nian", "chen")) + add(User(1L, "nian", "chen", UserStatusEnum.ACTIVE, null, UserRankEnum.VIP)) } fun add(user: User) { @@ -20,4 +22,8 @@ class UserRepository { fun update(user: User) { users[user.id] = user } + + fun findByUsername(firstName: String, lastName: String): User? { + return users.entries.find { it.value.firstName == firstName && it.value.lastName == lastName }?.value + } } \ No newline at end of file diff --git a/src/main/kotlin/mvc/entities/User.kt b/src/main/kotlin/mvc/entities/User.kt index 498c326..2a67c5b 100644 --- a/src/main/kotlin/mvc/entities/User.kt +++ b/src/main/kotlin/mvc/entities/User.kt @@ -1,7 +1,12 @@ package mvc.entities +import java.time.LocalDateTime + data class User ( val id: Long, - val firstName: String, - val lastName: String + var firstName: String, + var lastName: String, + var status: UserStatusEnum, + var lastUsernameChange: LocalDateTime?, + var rank: UserRankEnum, ) \ No newline at end of file diff --git a/src/main/kotlin/mvc/entities/UserRankEnum.kt b/src/main/kotlin/mvc/entities/UserRankEnum.kt new file mode 100644 index 0000000..c8bf755 --- /dev/null +++ b/src/main/kotlin/mvc/entities/UserRankEnum.kt @@ -0,0 +1,5 @@ +package mvc.entities + +enum class UserRankEnum { + REGULAR, VIP, INTERNAL +} \ No newline at end of file diff --git a/src/main/kotlin/mvc/entities/UserStatusEnum.kt b/src/main/kotlin/mvc/entities/UserStatusEnum.kt new file mode 100644 index 0000000..941bb20 --- /dev/null +++ b/src/main/kotlin/mvc/entities/UserStatusEnum.kt @@ -0,0 +1,5 @@ +package mvc.entities + +enum class UserStatusEnum { + ACTIVE,DEACTIVATED,BANNED +} \ No newline at end of file diff --git a/src/main/kotlin/mvc/services/UserComplexService.kt b/src/main/kotlin/mvc/services/UserComplexService.kt index 5b96561..ee6fd04 100644 --- a/src/main/kotlin/mvc/services/UserComplexService.kt +++ b/src/main/kotlin/mvc/services/UserComplexService.kt @@ -3,16 +3,89 @@ package mvc.services import shared.exceptions.NotFoundException import mvc.dao.UserRepository import mvc.entities.User +import mvc.entities.UserRankEnum +import mvc.entities.UserStatusEnum +import shared.dto.ChangeUsernameDto +import shared.exceptions.ChangeUsernameException +import java.time.Clock +import java.time.Duration +import java.time.LocalDateTime class UserComplexService( - val repository: UserRepository + val repository: UserRepository, ) : UserService { // 用户 - override fun changeUsername(user: User): User { - val findUser = repository.findById(user.id) ?: throw NotFoundException("User ${user.id} not found") - val copy = findUser.copy(firstName = user.firstName, lastName = user.lastName) - repository.update(copy) - return copy + override fun changeUsername(userDto: ChangeUsernameDto): User { + val user = repository.findById(userDto.id) ?: throw NotFoundException("用户${userDto.id}不存在") + // 判断用户状态 + if (user.status != UserStatusEnum.ACTIVE) throw ChangeUsernameException("用户未激活") + // 用户名长度 + if( !(user.firstName.length + user.lastName.length in 4..20)) { + throw ChangeUsernameException("用户名长度需在4-20之间") + } + + // 检测非法字符 + val regex = Regex("^[a-zA-Z0-9_]+$") + if(!regex.matches(userDto.firstName) || !regex.matches(userDto.lastName)) { + throw ChangeUsernameException("包含非法字符") + } + + // 检测权限 + var canChangeUsername = false + var requiresEmailVerification = false + var maxChangeIntervalDays = 0 + when (user.rank) { + UserRankEnum.REGULAR -> { + canChangeUsername = false + requiresEmailVerification = false + maxChangeIntervalDays = 0 + } + + UserRankEnum.VIP -> { + canChangeUsername = true + requiresEmailVerification = true + maxChangeIntervalDays = 30 + } + + UserRankEnum.INTERNAL -> { + canChangeUsername = true + requiresEmailVerification = false + maxChangeIntervalDays = 0 + } + } + + if (!canChangeUsername) throw ChangeUsernameException("用户没有权限修改用户名") + + if (requiresEmailVerification) { + val emailVerified = VerificationService().isVerified(user.id, userDto.verificationCode) + if (!emailVerified) { + throw ChangeUsernameException("验证码错误") + } + } + + user.lastUsernameChange?.let{ + val daysBetween = + Duration.between(user.lastUsernameChange, LocalDateTime.now(Clock.systemDefaultZone())).toDays() + if (daysBetween < maxChangeIntervalDays) { + throw ChangeUsernameException("30天内禁止重复修改") + } + } + + // 检测相同名 + if(user.firstName == userDto.firstName && user.lastName == userDto.lastName) { + throw ChangeUsernameException("不能修改为当前用户名") + } + val findByUsername = repository.findByUsername(userDto.firstName, userDto.lastName) + if(findByUsername != null) { + throw ChangeUsernameException("用户名已存在") + } + + + user.firstName = userDto.firstName + user.lastName = userDto.lastName + user.lastUsernameChange = LocalDateTime.now(Clock.systemDefaultZone()) + repository.update(user) + return user } } \ No newline at end of file diff --git a/src/main/kotlin/mvc/services/UserService.kt b/src/main/kotlin/mvc/services/UserService.kt index 68b06a4..1bae7ad 100644 --- a/src/main/kotlin/mvc/services/UserService.kt +++ b/src/main/kotlin/mvc/services/UserService.kt @@ -1,7 +1,8 @@ package mvc.services import mvc.entities.User +import shared.dto.ChangeUsernameDto interface UserService { - fun changeUsername(user: User): User + fun changeUsername(userDto: ChangeUsernameDto): User } \ No newline at end of file diff --git a/src/main/kotlin/mvc/services/UserSimpleService.kt b/src/main/kotlin/mvc/services/UserSimpleService.kt index bcf60a5..6b36391 100644 --- a/src/main/kotlin/mvc/services/UserSimpleService.kt +++ b/src/main/kotlin/mvc/services/UserSimpleService.kt @@ -3,12 +3,14 @@ package mvc.services import shared.exceptions.NotFoundException import mvc.dao.UserRepository import mvc.entities.User +import shared.dto.ChangeUsernameDto class UserSimpleService(val repository: UserRepository) : UserService { - override fun changeUsername(user: User): User { - val findUser = repository.findById(user.id) ?: throw NotFoundException("User ${user.id} not found") - val copy = findUser.copy(firstName = user.firstName, lastName = user.lastName) - repository.update(copy) - return copy + override fun changeUsername(userDto: ChangeUsernameDto): User { + val findUser = repository.findById(userDto.id) ?: throw NotFoundException("用户${userDto.id}不存在") + findUser.firstName = userDto.firstName + findUser.lastName = userDto.lastName + repository.update(findUser) + return findUser } } \ No newline at end of file diff --git a/src/main/kotlin/mvc/services/VerificationService.kt b/src/main/kotlin/mvc/services/VerificationService.kt new file mode 100644 index 0000000..d0b2532 --- /dev/null +++ b/src/main/kotlin/mvc/services/VerificationService.kt @@ -0,0 +1,7 @@ +package mvc.services + +class VerificationService { + fun isVerified(userId: Long, code: String): Boolean { + return "1234" == code + } +} \ No newline at end of file diff --git a/src/main/kotlin/shared/AggregateRoot.kt b/src/main/kotlin/shared/AggregateRoot.kt new file mode 100644 index 0000000..ef750c5 --- /dev/null +++ b/src/main/kotlin/shared/AggregateRoot.kt @@ -0,0 +1,5 @@ +package shared + +abstract class AggregateRoot{ + abstract val id: T +} \ No newline at end of file diff --git a/src/main/kotlin/shared/dto/ChangeUsernameDto.kt b/src/main/kotlin/shared/dto/ChangeUsernameDto.kt new file mode 100644 index 0000000..81be69e --- /dev/null +++ b/src/main/kotlin/shared/dto/ChangeUsernameDto.kt @@ -0,0 +1,3 @@ +package shared.dto + +data class ChangeUsernameDto(val id: Long, val firstName: String, val lastName: String, val verificationCode: String) \ No newline at end of file