Compare commits

...

4 Commits

Author SHA1 Message Date
klaus 86c627ed1d Merge pull request 'test' (#2) from test into main
Reviewed-on: #2
2023-08-18 12:54:09 +00:00
Klaus 409af8b18c 큐레이션 - member join 추가 2023-08-18 21:49:26 +09:00
Klaus 7b58335a42 닉네임 변경 로그 엔티티 - updated_at 제거 2023-08-18 19:25:58 +09:00
Klaus 25be1a6adc 프로필 수정 API 2023-08-18 19:15:19 +09:00
10 changed files with 306 additions and 0 deletions

View File

@ -340,6 +340,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
)
)
.from(audioContent)
.innerJoin(audioContent.member, member)
.where(where)
.orderBy(audioContent.id.desc())
.fetch()

View File

@ -0,0 +1,3 @@
package kr.co.vividnext.sodalive.member
data class GetChangeNicknamePriceResponse(val price: Int)

View File

@ -22,6 +22,18 @@ import org.springframework.web.multipart.MultipartFile
@RestController
@RequestMapping("/member")
class MemberController(private val service: MemberService) {
@GetMapping("/check/email")
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
@GetMapping("/check/nickname")
fun checkNickname(@RequestParam nickname: String) = service.duplicateCheckNickname(nickname)
@PutMapping("/change/nickname")
fun changeNickname(
@RequestBody profileUpdateRequest: ProfileUpdateRequest,
@AuthenticationPrincipal user: User
) = ApiResponse.ok(service.updateNickname(profileUpdateRequest, user))
@PostMapping("/signup")
fun signUp(
@RequestPart("profileImage", required = false) profileImage: MultipartFile? = null,
@ -50,6 +62,16 @@ class MemberController(private val service: MemberService) {
ApiResponse.ok(service.logoutAll(member.id!!))
}
@GetMapping
fun getMember(
@RequestParam container: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(service.getMember(member.id!!, container))
}
@GetMapping("/info")
fun getMemberInfo(
@RequestParam container: String?,
@ -151,4 +173,24 @@ class MemberController(private val service: MemberService) {
@RequestBody signOutRequest: SignOutRequest,
@AuthenticationPrincipal user: User
) = ApiResponse.ok(service.signOut(signOutRequest, user), "정상적으로 탈퇴 처리되었습니다.")
@GetMapping("/change/nickname/price")
fun getChangeNicknamePrice(
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(service.getChangeNicknamePrice(memberId = member.id!!))
}
@PutMapping
fun profileUpdate(
@RequestBody profileUpdateRequest: ProfileUpdateRequest,
@AuthenticationPrincipal user: User
) = ApiResponse.ok(service.profileUpdate(profileUpdateRequest, user))
@PostMapping("/image")
fun profileImageUpdate(
@RequestParam("image") multipartFile: MultipartFile,
@AuthenticationPrincipal user: User
) = ApiResponse.ok(service.profileImageUpdate(multipartFile, user))
}

View File

@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.member.QMember.member
import kr.co.vividnext.sodalive.member.auth.QAuth.auth
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing
import kr.co.vividnext.sodalive.member.nickname.QNicknameChangeLog.nicknameChangeLog
import kr.co.vividnext.sodalive.member.notification.QMemberNotification.memberNotification
import kr.co.vividnext.sodalive.message.QMessage.message
import org.springframework.data.jpa.repository.JpaRepository
@ -37,6 +38,7 @@ interface MemberQueryRepository {
fun getMessageRecipientPushToken(messageId: Long): GetMessageRecipientPushTokenResponse
fun getIndividualRecipientPushTokens(recipients: List<Long>, isAuth: Boolean): Map<String, List<List<String>>>
fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse
}
@Repository
@ -210,4 +212,21 @@ class MemberQueryRepositoryImpl(
return mapOf("aos" to aosPushTokens, "ios" to iosPushTokens)
}
override fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse {
val changeCount = queryFactory
.select(nicknameChangeLog.id)
.from(nicknameChangeLog)
.where(nicknameChangeLog.member.id.eq(memberId))
.fetch()
.count()
return GetChangeNicknamePriceResponse(
price = if (changeCount > 0) {
1000
} else {
0
}
)
}
}

View File

@ -3,6 +3,8 @@ package kr.co.vividnext.sodalive.member
import com.amazonaws.services.s3.model.ObjectMetadata
import com.fasterxml.jackson.databind.ObjectMapper
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
import kr.co.vividnext.sodalive.can.payment.CanPaymentService
import kr.co.vividnext.sodalive.can.use.CanUsage
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.jwt.TokenProvider
@ -16,6 +18,8 @@ import kr.co.vividnext.sodalive.member.info.GetMemberInfoResponse
import kr.co.vividnext.sodalive.member.login.LoginRequest
import kr.co.vividnext.sodalive.member.login.LoginResponse
import kr.co.vividnext.sodalive.member.myPage.MyPageResponse
import kr.co.vividnext.sodalive.member.nickname.NicknameChangeLog
import kr.co.vividnext.sodalive.member.nickname.NicknameChangeLogRepository
import kr.co.vividnext.sodalive.member.notification.MemberNotificationService
import kr.co.vividnext.sodalive.member.notification.UpdateNotificationSettingRequest
import kr.co.vividnext.sodalive.member.signUp.SignUpRequest
@ -25,6 +29,8 @@ import kr.co.vividnext.sodalive.member.stipulation.StipulationAgree
import kr.co.vividnext.sodalive.member.stipulation.StipulationAgreeRepository
import kr.co.vividnext.sodalive.member.stipulation.StipulationIds
import kr.co.vividnext.sodalive.member.stipulation.StipulationRepository
import kr.co.vividnext.sodalive.member.tag.MemberCreatorTag
import kr.co.vividnext.sodalive.member.tag.MemberTagRepository
import kr.co.vividnext.sodalive.member.token.MemberTokenRepository
import kr.co.vividnext.sodalive.utils.generateFileName
import org.springframework.beans.factory.annotation.Value
@ -53,7 +59,10 @@ class MemberService(
private val creatorFollowingRepository: CreatorFollowingRepository,
private val blockMemberRepository: BlockMemberRepository,
private val signOutRepository: SignOutRepository,
private val nicknameChangeLogRepository: NicknameChangeLogRepository,
private val memberTagRepository: MemberTagRepository,
private val canPaymentService: CanPaymentService,
private val memberNotificationService: MemberNotificationService,
private val s3Uploader: S3Uploader,
@ -107,6 +116,13 @@ class MemberService(
)
}
fun getMember(id: Long, container: String): ProfileResponse {
val member = repository.findByIdOrNull(id)
?: throw SodaException("없는 사용자 입니다.")
return ProfileResponse(member, cloudFrontHost, container)
}
fun getMemberInfo(member: Member, container: String): GetMemberInfoResponse {
return GetMemberInfoResponse(
can = member.getChargeCan(container) + member.getRewardCan(container),
@ -406,6 +422,130 @@ class MemberService(
signOutRepository.save(signOut)
}
fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse {
return repository.getChangeNicknamePrice(memberId = memberId)
}
@Transactional
fun updateNickname(profileUpdateRequest: ProfileUpdateRequest, user: User) {
if (profileUpdateRequest.email != user.username) {
throw SodaException("로그인 정보를 확인해 주세요.")
}
val member = repository.findByEmail(user.username) ?: throw SodaException("로그인 정보를 확인해주세요.")
if (profileUpdateRequest.nickname != null) {
validateNickname(profileUpdateRequest.nickname)
repository.findByNickname(profileUpdateRequest.nickname)
?.let { throw SodaException("이미 사용중인 닉네임 입니다.") }
val price = repository.getChangeNicknamePrice(memberId = member.id!!).price
if (price > 0) {
canPaymentService.spendCan(
memberId = member.id!!,
needCan = price,
canUsage = CanUsage.CHANGE_NICKNAME,
container = profileUpdateRequest.container
)
}
val nicknameChangeLog = NicknameChangeLog(prevNickname = member.nickname)
nicknameChangeLog.member = member
nicknameChangeLogRepository.save(nicknameChangeLog)
member.nickname = profileUpdateRequest.nickname
}
}
@Transactional
fun profileUpdate(profileUpdateRequest: ProfileUpdateRequest, user: User): ProfileResponse {
if (profileUpdateRequest.email != user.username) {
throw SodaException("로그인 정보를 확인해 주세요.")
}
val member = repository.findByEmail(user.username) ?: throw SodaException("로그인 정보를 확인해주세요.")
if (profileUpdateRequest.modifyPassword != null) {
if (passwordEncoder.matches(profileUpdateRequest.password, member.password)) {
validatePassword(profileUpdateRequest.modifyPassword)
member.password = passwordEncoder.encode(profileUpdateRequest.modifyPassword)
} else {
throw SodaException("비밀번호가 일치하지 않습니다.")
}
}
if (profileUpdateRequest.gender != null) {
member.gender = profileUpdateRequest.gender
}
if (profileUpdateRequest.nickname != null) {
validateNickname(profileUpdateRequest.nickname)
repository.findByNickname(profileUpdateRequest.nickname)
?.let { throw SodaException("이미 사용중인 닉네임 입니다.") }
member.nickname = profileUpdateRequest.nickname
}
val tags = if (!profileUpdateRequest.removeTags.isNullOrEmpty()) {
member.tags.filter { !profileUpdateRequest.removeTags.contains(it.tag.tag) }
} else {
member.tags
}.toMutableList()
if (!profileUpdateRequest.insertTags.isNullOrEmpty()) {
val accountCounselorTags = memberTagRepository.findByMember(member).map { it.tag }
profileUpdateRequest.insertTags.forEach {
val tag = memberTagRepository.findByTag(it)
if (tag != null && !accountCounselorTags.contains(tag)) {
tags.add(MemberCreatorTag(member, tag))
}
}
}
if (tags != member.tags) {
member.tags.clear()
member.tags.addAll(tags)
}
if (profileUpdateRequest.introduce != null) {
member.introduce = profileUpdateRequest.introduce
}
if (profileUpdateRequest.youtubeUrl != null) {
member.youtubeUrl = profileUpdateRequest.youtubeUrl
}
if (profileUpdateRequest.instagramUrl != null) {
member.instagramUrl = profileUpdateRequest.instagramUrl
}
if (profileUpdateRequest.websiteUrl != null) {
member.websiteUrl = profileUpdateRequest.websiteUrl
}
if (profileUpdateRequest.blogUrl != null) {
member.blogUrl = profileUpdateRequest.blogUrl
}
return ProfileResponse(member, cloudFrontHost, profileUpdateRequest.container)
}
@Transactional
fun profileImageUpdate(multipartFile: MultipartFile, user: User): String {
val member = repository.findByEmail(user.username) ?: throw SodaException("로그인 정보를 확인해주세요.")
val metadata = ObjectMetadata()
metadata.contentLength = multipartFile.size
member.profileImage = s3Uploader.upload(
inputStream = multipartFile.inputStream,
bucket = s3Bucket,
filePath = "profile/${member.id}/${generateFileName(prefix = "${member.id}-profile")}",
metadata = metadata
)
return "$cloudFrontHost/${member.profileImage!!}"
}
private fun getOrCreateLock(memberId: Long): ReentrantReadWriteLock {
return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() }
}

View File

@ -0,0 +1,37 @@
package kr.co.vividnext.sodalive.member
data class ProfileResponse(
val userId: Long,
val email: String,
val nickname: String,
val gender: Gender,
val profileUrl: String,
val chargeCan: Int,
val rewardCan: Int,
val youtubeUrl: String?,
val instagramUrl: String?,
val blogUrl: String?,
val websiteUrl: String?,
val introduce: String,
val tags: List<String>
) {
constructor(member: Member, cloudFrontHost: String, container: String) : this(
userId = member.id!!,
email = member.email,
nickname = member.nickname,
gender = member.gender,
profileUrl = if (member.profileImage != null) {
"$cloudFrontHost/${member.profileImage}"
} else {
"$cloudFrontHost/profile/default-profile.png"
},
chargeCan = member.getChargeCan(container),
rewardCan = member.getRewardCan(container),
youtubeUrl = member.youtubeUrl,
instagramUrl = member.instagramUrl,
websiteUrl = member.websiteUrl,
blogUrl = member.blogUrl,
introduce = member.introduce,
tags = member.tags.asSequence().filter { it.tag.isActive }.map { it.tag.tag }.toList()
)
}

View File

@ -0,0 +1,17 @@
package kr.co.vividnext.sodalive.member
data class ProfileUpdateRequest(
val email: String,
val password: String? = null,
val modifyPassword: String? = null,
val nickname: String? = null,
val gender: Gender? = null,
val insertTags: List<String>? = null,
val removeTags: List<String>? = null,
val introduce: String? = null,
val youtubeUrl: String? = null,
val instagramUrl: String? = null,
val websiteUrl: String? = null,
val blogUrl: String? = null,
val container: String
)

View File

@ -0,0 +1,30 @@
package kr.co.vividnext.sodalive.member.nickname
import kr.co.vividnext.sodalive.member.Member
import java.time.LocalDateTime
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
import javax.persistence.PrePersist
@Entity
data class NicknameChangeLog(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
val prevNickname: String,
var createdAt: LocalDateTime? = null
) {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
var member: Member? = null
@PrePersist
fun prePersist() {
createdAt = LocalDateTime.now()
}
}

View File

@ -0,0 +1,7 @@
package kr.co.vividnext.sodalive.member.nickname
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface NicknameChangeLogRepository : JpaRepository<NicknameChangeLog, Long>

View File

@ -1,7 +1,9 @@
package kr.co.vividnext.sodalive.member.tag
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.tag.QCreatorTag.creatorTag
import kr.co.vividnext.sodalive.member.tag.QMemberCreatorTag.memberCreatorTag
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.jpa.repository.JpaRepository
@ -11,6 +13,7 @@ interface MemberTagRepository : JpaRepository<CreatorTag, Long>, MemberTagQueryR
interface MemberTagQueryRepository {
fun getTags(): List<GetMemberTagResponse>
fun findByMember(member: Member): List<MemberCreatorTag>
}
class MemberTagQueryRepositoryImpl(
@ -33,4 +36,11 @@ class MemberTagQueryRepositoryImpl(
.orderBy(creatorTag.orders.asc())
.fetch()
}
override fun findByMember(member: Member): List<MemberCreatorTag> {
return queryFactory
.selectFrom(memberCreatorTag)
.where(memberCreatorTag.member.id.eq(member.id))
.fetch()
}
}