Skip to content

Commit

Permalink
✨ 프로필 이미지 업로드, 삭제, 조회 API 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
waterfogSW committed Dec 7, 2024
1 parent 0b8efb3 commit 3995126
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.threedays.application.user.port.inbound

import com.threedays.domain.user.entity.User
import com.threedays.domain.user.entity.UserProfileImage

interface DeleteUserProfileImage {

fun invoke(command: Command)

data class Command(
val userId: User.Id,
val imageId: UserProfileImage.Id,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ interface UserProfileImagePort {
extension: UserProfileImage.Extension,
): URL

fun deleteImageById(
id: UserProfileImage.Id,
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.threedays.application.auth.config.UserProperties
import com.threedays.application.auth.port.inbound.IssueLoginTokens
import com.threedays.application.user.port.inbound.CompleteUserProfileImageUpload
import com.threedays.application.user.port.inbound.DeleteProfileWidget
import com.threedays.application.user.port.inbound.DeleteUserProfileImage
import com.threedays.application.user.port.inbound.GetUserProfileImageUploadUrl
import com.threedays.application.user.port.inbound.PutProfileWidget
import com.threedays.application.user.port.inbound.RegisterUser
Expand Down Expand Up @@ -32,7 +33,7 @@ class UserService(
private val authProperties: AuthProperties,
private val userProperties: UserProperties,
) : RegisterUser, PutProfileWidget, DeleteProfileWidget, UpdateUserInfo, UpdateDesiredPartner,
GetUserProfileImageUploadUrl, CompleteUserProfileImageUpload {
GetUserProfileImageUploadUrl, CompleteUserProfileImageUpload, DeleteUserProfileImage {

@Transactional
override fun invoke(command: RegisterUser.Command): RegisterUser.Result {
Expand Down Expand Up @@ -130,10 +131,18 @@ class UserService(
userRepository
.get(command.userId)
.updateUserProfileImage(
id = command.imageId,
extension = command.extension,
getProfileImageUrlAction = userProfileImagePort::findImageUrlByIdAndExtension,
)
.let { userRepository.save(it) }
}

override fun invoke(command: DeleteUserProfileImage.Command) {
userRepository
.get(command.userId)
.deleteUserProfileImage(command.imageId, userProfileImagePort::deleteImageById)
.let { userRepository.save(it) }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.threedays.bootstrap.api.user

import com.threedays.application.user.port.inbound.CompleteUserProfileImageUpload
import com.threedays.application.user.port.inbound.DeleteProfileWidget
import com.threedays.application.user.port.inbound.DeleteUserProfileImage
import com.threedays.application.user.port.inbound.GetUserProfileImageUploadUrl
import com.threedays.application.user.port.inbound.PutProfileWidget
import com.threedays.application.user.port.inbound.RegisterUser
Expand All @@ -25,6 +26,7 @@ import com.threedays.oas.model.GetProfileImageUploadUrlResponse
import com.threedays.oas.model.JobOccupationDisplayInfo
import com.threedays.oas.model.LocationDisplayInfo
import com.threedays.oas.model.PreferDistance
import com.threedays.oas.model.ProfileImage
import com.threedays.oas.model.ProfileImageExtension
import com.threedays.oas.model.ProfileWidget
import com.threedays.oas.model.ProfileWidgetType
Expand All @@ -42,6 +44,7 @@ import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RestController
import java.time.Year
import java.util.UUID

@RestController
class UserController(
Expand All @@ -53,6 +56,7 @@ class UserController(
private val updateDesiredPartner: UpdateDesiredPartner,
private val getUserProfileImageUploadUrl: GetUserProfileImageUploadUrl,
private val completeUserProfileImageUpload: CompleteUserProfileImageUpload,
private val deleteUserProfileImage: DeleteUserProfileImage,
) : UsersApi {

override fun registerUser(
Expand Down Expand Up @@ -104,6 +108,13 @@ class UserController(
GetMyUserInfoResponse(
id = user.id.value,
name = user.name.value,
profileImages = user.profileImages.map {
ProfileImage(
id = it.id.value,
url = it.url.toURI(),
extension = ProfileImageExtension.valueOf(it.extension.name)
)
},
phoneNumber = user.phoneNumber.value,
profile = UserProfileDisplayInfo(
gender = com.threedays.oas.model.Gender.valueOf(user.profile.gender.name),
Expand Down Expand Up @@ -264,6 +275,16 @@ class UserController(
ResponseEntity.ok().build()
}

override fun deleteProfileImage(imageId: UUID): ResponseEntity<Unit> =
withUserAuthentication { authentication ->
val command = DeleteUserProfileImage.Command(
userId = authentication.userId,
imageId = UUIDTypeId.from(imageId)
)
deleteUserProfileImage.invoke(command)
ResponseEntity.noContent().build()
}

override fun getProfileImageUploadUrl(extension: ProfileImageExtension): ResponseEntity<GetProfileImageUploadUrlResponse> = withUserAuthentication { _ ->
val command: GetUserProfileImageUploadUrl.Command = GetUserProfileImageUploadUrl.Command(
extension = UserProfileImage.Extension.valueOf(extension.name)
Expand Down
26 changes: 24 additions & 2 deletions domain/src/main/kotlin/com/threedays/domain/user/entity/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import com.threedays.domain.user.vo.Gender
import com.threedays.domain.user.vo.JobOccupation
import com.threedays.support.common.base.domain.AggregateRoot
import com.threedays.support.common.base.domain.UUIDTypeId
import com.threedays.support.common.exception.NotFoundException
import java.net.URL
import java.time.Year
import java.util.*


/**
* User Entity
*/
data class User(
override val id: Id,
val name: Name,
Expand Down Expand Up @@ -141,11 +144,30 @@ data class User(

// TODO: 이미지 여러개 업로드 가능하도록 수정 필요
fun updateUserProfileImage(
id: UserProfileImage.Id,
extension: Extension,
getProfileImageUrlAction: (UserProfileImage.Id, Extension) -> URL,
): User {
val newProfileImage = UserProfileImage.create(extension, getProfileImageUrlAction)
val newProfileImage = UserProfileImage(
id = id,
extension = extension,
url = getProfileImageUrlAction(id, extension),
)
return copy(profileImages = listOf(newProfileImage))
}

fun deleteUserProfileImage(
imageId: UserProfileImage.Id,
deleteProfileImageAction: (UserProfileImage.Id) -> Unit,
): User {
val targetProfileImage: UserProfileImage = profileImages.find { it.id == imageId }
?: throw NotFoundException("해당 프로필 이미지를 찾을 수 없습니다.")

deleteProfileImageAction(targetProfileImage.id)

val updatedProfileImages: List<UserProfileImage> = profileImages.filter { it.id != imageId }

return copy(profileImages = updatedProfileImages)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,4 @@ class UserProfileImage(
)
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.threedays.aws.s3.user
import com.threedays.application.user.port.outbound.UserProfileImagePort
import com.threedays.aws.s3.config.S3Properties
import com.threedays.domain.user.entity.UserProfileImage
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Component
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.GetUrlRequest
Expand All @@ -21,6 +22,12 @@ class UserProfileImageS3Adapter(
private val s3Properties: S3Properties,
) : UserProfileImagePort {

companion object {

private val logger = KotlinLogging.logger { }
}


override fun getUploadUrl(
id: UUID,
extension: UserProfileImage.Extension,
Expand Down Expand Up @@ -51,8 +58,7 @@ class UserProfileImageS3Adapter(

override fun findImageUrlByIdAndExtension(
id: UserProfileImage.Id,
extension: UserProfileImage.Extension

extension: UserProfileImage.Extension,
): URL {
val getObjectRequest = GetUrlRequest
.builder()
Expand All @@ -65,6 +71,23 @@ class UserProfileImageS3Adapter(
.getUrl(getObjectRequest)
}

override fun deleteImageById(id: UserProfileImage.Id) {
val bucketName = s3Properties.userProfileImage.bucketName
val objectKey = getObjectKey(id.value, UserProfileImage.Extension.PNG)

try {
logger.debug { "[S3] Deleting object from bucket: $bucketName, key: $objectKey" }
val response = s3Client.deleteObject { builder ->
builder.bucket(bucketName)
builder.key(objectKey)
}
logger.debug { "[S3] Delete response: $response" }
} catch (e: Exception) {
logger.error { "[S3] Error deleting object: ${e.message}" }
throw e
}
}

private fun getObjectKey(
id: UUID,
extension: UserProfileImage.Extension,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ class UserJpaEntity(
var phoneNumber: String = phoneNumber
private set

@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id")
@OneToMany(
fetch = FetchType.EAGER,
cascade = [CascadeType.ALL],
orphanRemoval = true
)
@JoinColumn(name = "user_id", nullable = false)
@OrderColumn(name = "image_order")
var profileImages: List<UserProfileImageJpaEntity> = profileImages
private set
Expand All @@ -62,14 +66,17 @@ class UserJpaEntity(

companion object {

fun User.toJpaEntity() = UserJpaEntity(
id = id.value,
name = name.value,
phoneNumber = phoneNumber.value,
profileImages = profileImages.map { it.toJpaEntity() },
profile = profile.toJpaEntity(),
desiredPartner = desiredPartner.toJpaEntity(),
)
fun User.toJpaEntity(): UserJpaEntity {
val entity = UserJpaEntity(
id = id.value,
name = name.value,
phoneNumber = phoneNumber.value,
profileImages = profileImages.map { it.toJpaEntity() },
profile = profile.toJpaEntity(),
desiredPartner = desiredPartner.toJpaEntity(),
)
return entity
}
}

fun toDomainEntity(): User {
Expand Down
2 changes: 1 addition & 1 deletion openapi
Submodule openapi updated 1 files
+49 −0 openapi.yaml

0 comments on commit 3995126

Please sign in to comment.