| @@ -340,6 +340,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | ||||
|                 ) | ||||
|             ) | ||||
|             .from(audioContent) | ||||
|             .innerJoin(audioContent.member, member) | ||||
|             .where(where) | ||||
|             .orderBy(audioContent.id.desc()) | ||||
|             .fetch() | ||||
|   | ||||
| @@ -0,0 +1,3 @@ | ||||
| package kr.co.vividnext.sodalive.member | ||||
|  | ||||
| data class GetChangeNicknamePriceResponse(val price: Int) | ||||
| @@ -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)) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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() } | ||||
|     } | ||||
|   | ||||
| @@ -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() | ||||
|     ) | ||||
| } | ||||
| @@ -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 | ||||
| ) | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
| @@ -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> | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user