test #312
| @@ -4,6 +4,7 @@ import com.querydsl.core.types.dsl.Expressions | |||||||
| import com.querydsl.jpa.impl.JPAQueryFactory | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
| import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | import kr.co.vividnext.sodalive.content.QAudioContent.audioContent | ||||||
| import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment | import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment | ||||||
|  | import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken | ||||||
| import kr.co.vividnext.sodalive.member.QMember.member | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
| import org.springframework.data.jpa.repository.JpaRepository | import org.springframework.data.jpa.repository.JpaRepository | ||||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||||
| @@ -204,25 +205,27 @@ class AudioContentCommentQueryRepositoryImpl( | |||||||
|             queryFactory |             queryFactory | ||||||
|                 .select( |                 .select( | ||||||
|                     QFindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse( |                     QFindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse( | ||||||
|                         member.pushToken.coalesce(""), |                         pushToken.token, | ||||||
|                         member.container |                         pushToken.deviceType | ||||||
|                     ) |                     ) | ||||||
|                 ) |                 ) | ||||||
|                 .from(audioContentComment) |                 .from(audioContentComment) | ||||||
|                 .innerJoin(audioContentComment.audioContent, audioContent) |                 .innerJoin(audioContentComment.audioContent, audioContent) | ||||||
|                 .innerJoin(audioContentComment.member, member) |                 .innerJoin(audioContentComment.member, member) | ||||||
|  |                 .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|                 .where(where) |                 .where(where) | ||||||
|                 .fetch() |                 .fetch() | ||||||
|         } else { |         } else { | ||||||
|             queryFactory |             queryFactory | ||||||
|                 .select( |                 .select( | ||||||
|                     QFindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse( |                     QFindPushTokenByContentIdAndCommentParentIdMyMemberIdResponse( | ||||||
|                         member.pushToken.coalesce(""), |                         pushToken.token, | ||||||
|                         member.container |                         pushToken.deviceType | ||||||
|                     ) |                     ) | ||||||
|                 ) |                 ) | ||||||
|                 .from(audioContent) |                 .from(audioContent) | ||||||
|                 .innerJoin(audioContent.member, member) |                 .innerJoin(audioContent.member, member) | ||||||
|  |                 .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|                 .where(where) |                 .where(where) | ||||||
|                 .fetch() |                 .fetch() | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ import org.springframework.scheduling.annotation.Async | |||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
|  |  | ||||||
| @Service | @Service | ||||||
| class FcmService { | class FcmService(private val pushTokenService: PushTokenService) { | ||||||
|     private val logger = LoggerFactory.getLogger(this::class.java) |     private val logger = LoggerFactory.getLogger(this::class.java) | ||||||
|  |  | ||||||
|     @Async |     @Async | ||||||
| @@ -100,7 +100,8 @@ class FcmService { | |||||||
|  |  | ||||||
|                     if (exception?.messagingErrorCode == MessagingErrorCode.UNREGISTERED) { |                     if (exception?.messagingErrorCode == MessagingErrorCode.UNREGISTERED) { | ||||||
|                         logger.error("[FCM] ❌ UNREGISTERED → $token") |                         logger.error("[FCM] ❌ UNREGISTERED → $token") | ||||||
|                         // 필요 시 DB에서 삭제 |                         // DB에서 삭제 | ||||||
|  |                         pushTokenService.unregisterInvalidToken(token) | ||||||
|                     } else { |                     } else { | ||||||
|                         logger.error("[FCM] ❌ 실패: $token / ${exception?.messagingErrorCode}") |                         logger.error("[FCM] ❌ 실패: $token / ${exception?.messagingErrorCode}") | ||||||
|                         failedTokens.add(token) |                         failedTokens.add(token) | ||||||
| @@ -148,6 +149,8 @@ class FcmService { | |||||||
|                 // "registration-token-not-registered" 예외 코드 확인 |                 // "registration-token-not-registered" 예외 코드 확인 | ||||||
|                 if (e.messagingErrorCode == MessagingErrorCode.UNREGISTERED) { |                 if (e.messagingErrorCode == MessagingErrorCode.UNREGISTERED) { | ||||||
|                     logger.error("[FCM] ❌ 실패: 토큰이 등록되지 않음 (등록 해제됨) → 재시도 안함") |                     logger.error("[FCM] ❌ 실패: 토큰이 등록되지 않음 (등록 해제됨) → 재시도 안함") | ||||||
|  |                     // DB에서 삭제 | ||||||
|  |                     pushTokenService.unregisterInvalidToken(token) | ||||||
|                     return |                     return | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								src/main/kotlin/kr/co/vividnext/sodalive/fcm/PushToken.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/main/kotlin/kr/co/vividnext/sodalive/fcm/PushToken.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.common.BaseEntity | ||||||
|  | import kr.co.vividnext.sodalive.member.Member | ||||||
|  | import javax.persistence.Entity | ||||||
|  | import javax.persistence.FetchType | ||||||
|  | import javax.persistence.JoinColumn | ||||||
|  | import javax.persistence.ManyToOne | ||||||
|  |  | ||||||
|  | @Entity | ||||||
|  | data class PushToken( | ||||||
|  |     var token: String, | ||||||
|  |     var deviceType: String | ||||||
|  | ) : BaseEntity() { | ||||||
|  |     @ManyToOne(fetch = FetchType.LAZY) | ||||||
|  |     @JoinColumn(name = "member_id", nullable = true) | ||||||
|  |     var member: Member? = null | ||||||
|  | } | ||||||
| @@ -0,0 +1,30 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
|  | import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken | ||||||
|  | import org.springframework.data.jpa.repository.JpaRepository | ||||||
|  |  | ||||||
|  | interface PushTokenRepository : JpaRepository<PushToken, Long>, PushTokenQueryRepository | ||||||
|  |  | ||||||
|  | interface PushTokenQueryRepository { | ||||||
|  |     fun findByToken(token: String): PushToken? | ||||||
|  |     fun findByMemberId(memberId: Long): List<PushToken> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class PushTokenQueryRepositoryImpl( | ||||||
|  |     private val queryFactory: JPAQueryFactory | ||||||
|  | ) : PushTokenQueryRepository { | ||||||
|  |     override fun findByToken(token: String): PushToken? { | ||||||
|  |         return queryFactory | ||||||
|  |             .selectFrom(pushToken) | ||||||
|  |             .where(pushToken.token.eq(token)) | ||||||
|  |             .fetchFirst() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun findByMemberId(memberId: Long): List<PushToken> { | ||||||
|  |         return queryFactory | ||||||
|  |             .selectFrom(pushToken) | ||||||
|  |             .where(pushToken.member.id.eq(memberId)) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | package kr.co.vividnext.sodalive.fcm | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.member.MemberRepository | ||||||
|  | import org.springframework.data.repository.findByIdOrNull | ||||||
|  | import org.springframework.stereotype.Service | ||||||
|  | import org.springframework.transaction.annotation.Transactional | ||||||
|  |  | ||||||
|  | @Service | ||||||
|  | class PushTokenService( | ||||||
|  |     private val repository: PushTokenRepository, | ||||||
|  |     private val memberRepository: MemberRepository | ||||||
|  | ) { | ||||||
|  |     @Transactional | ||||||
|  |     fun registerToken(memberId: Long, token: String, deviceType: String) { | ||||||
|  |         val member = memberRepository.findByIdOrNull(memberId) | ||||||
|  |  | ||||||
|  |         if (member != null) { | ||||||
|  |             val existing = repository.findByToken(token) | ||||||
|  |             if (existing != null) { | ||||||
|  |                 existing.member = member | ||||||
|  |                 existing.token = token | ||||||
|  |                 existing.deviceType = deviceType | ||||||
|  |             } else { | ||||||
|  |                 val newToken = PushToken(token, deviceType) | ||||||
|  |                 newToken.member = member | ||||||
|  |                 repository.save(newToken) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun logout(memberId: Long) { | ||||||
|  |         val tokens = repository.findByMemberId(memberId) | ||||||
|  |  | ||||||
|  |         for (token in tokens) { | ||||||
|  |             token.member = null | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Transactional | ||||||
|  |     fun unregisterInvalidToken(token: String) { | ||||||
|  |         val existing = repository.findByToken(token) | ||||||
|  |         if (existing != null) { | ||||||
|  |             repository.delete(existing) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.member | |||||||
| import com.querydsl.jpa.impl.JPAQueryFactory | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
| import kr.co.vividnext.sodalive.fcm.GetMessageRecipientPushTokenResponse | import kr.co.vividnext.sodalive.fcm.GetMessageRecipientPushTokenResponse | ||||||
| import kr.co.vividnext.sodalive.fcm.QGetMessageRecipientPushTokenResponse | import kr.co.vividnext.sodalive.fcm.QGetMessageRecipientPushTokenResponse | ||||||
|  | import kr.co.vividnext.sodalive.fcm.QPushToken.pushToken | ||||||
| import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation | import kr.co.vividnext.sodalive.live.reservation.QLiveReservation.liveReservation | ||||||
| import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom | import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom | ||||||
| import kr.co.vividnext.sodalive.member.QMember.member | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
| @@ -26,7 +27,6 @@ interface MemberRepository : JpaRepository<Member, Long>, MemberQueryRepository | |||||||
| } | } | ||||||
|  |  | ||||||
| interface MemberQueryRepository { | interface MemberQueryRepository { | ||||||
|     fun findByPushToken(pushToken: String): List<Member> |  | ||||||
|     fun findByNicknameAndOtherCondition(nickname: String, member: Member): List<Member> |     fun findByNicknameAndOtherCondition(nickname: String, member: Member): List<Member> | ||||||
|     fun findCreatorByIdOrNull(memberId: Long): Member? |     fun findCreatorByIdOrNull(memberId: Long): Member? | ||||||
|     fun getAllRecipientPushTokens(isAuth: Boolean?, container: String): List<List<String>> |     fun getAllRecipientPushTokens(isAuth: Boolean?, container: String): List<List<String>> | ||||||
| @@ -74,13 +74,6 @@ class MemberQueryRepositoryImpl( | |||||||
|     @Value("\${cloud.aws.cloud-front.host}") |     @Value("\${cloud.aws.cloud-front.host}") | ||||||
|     private val cloudFrontHost: String |     private val cloudFrontHost: String | ||||||
| ) : MemberQueryRepository { | ) : MemberQueryRepository { | ||||||
|     override fun findByPushToken(pushToken: String): List<Member> { |  | ||||||
|         return queryFactory |  | ||||||
|             .selectFrom(member) |  | ||||||
|             .where(member.pushToken.eq(pushToken)) |  | ||||||
|             .fetch() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun findByNicknameAndOtherCondition(nickname: String, member: Member): List<Member> { |     override fun findByNicknameAndOtherCondition(nickname: String, member: Member): List<Member> { | ||||||
|         var where = QMember.member.nickname.containsIgnoreCase(nickname) |         var where = QMember.member.nickname.containsIgnoreCase(nickname) | ||||||
|             .and(QMember.member.id.ne(member.id)) |             .and(QMember.member.id.ne(member.id)) | ||||||
| @@ -111,8 +104,7 @@ class MemberQueryRepositoryImpl( | |||||||
|     override fun getAllRecipientPushTokens(isAuth: Boolean?, container: String): List<List<String>> { |     override fun getAllRecipientPushTokens(isAuth: Boolean?, container: String): List<List<String>> { | ||||||
|         var where = member.isActive.isTrue |         var where = member.isActive.isTrue | ||||||
|             .and(member.email.notIn("admin@sodalive.net")) |             .and(member.email.notIn("admin@sodalive.net")) | ||||||
|             .and(member.container.eq(container)) |             .and(pushToken.deviceType.eq(container)) | ||||||
|             .and(member.pushToken.isNotNull) |  | ||||||
|  |  | ||||||
|         if (isAuth != null) { |         if (isAuth != null) { | ||||||
|             where = if (isAuth) { |             where = if (isAuth) { | ||||||
| @@ -123,8 +115,9 @@ class MemberQueryRepositoryImpl( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return queryFactory |         return queryFactory | ||||||
|             .select(member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(member) |             .from(member) | ||||||
|  |             .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(member.auth, auth) |             .leftJoin(member.auth, auth) | ||||||
|             .where(where) |             .where(where) | ||||||
|             .fetch() |             .fetch() | ||||||
| @@ -142,18 +135,18 @@ class MemberQueryRepositoryImpl( | |||||||
|         val creator = QMember.member |         val creator = QMember.member | ||||||
|  |  | ||||||
|         var where = creatorFollowing.isActive.isTrue |         var where = creatorFollowing.isActive.isTrue | ||||||
|  |             .and(creatorFollowing.member.isActive.isTrue) | ||||||
|             .and(creatorFollowing.creator.id.eq(creatorId)) |             .and(creatorFollowing.creator.id.eq(creatorId)) | ||||||
|             .and(creatorFollowing.member.notification.live.isTrue) |             .and(creatorFollowing.member.notification.live.isTrue) | ||||||
|             .and(creatorFollowing.member.container.eq(container)) |  | ||||||
|             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) |             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) | ||||||
|             .and( |             .and( | ||||||
|                 creatorFollowing.member.id.notIn( |                 creatorFollowing.member.id.notIn( | ||||||
|                     blockMemberRepository.getBlockedMemberIdList(creatorId) |                     blockMemberRepository.getBlockedMemberIdList(creatorId) | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .and(creatorFollowing.member.pushToken.isNotNull) |  | ||||||
|             .and(creatorFollowing.isNotify.isTrue) |             .and(creatorFollowing.isNotify.isTrue) | ||||||
|             .or(member.id.eq(4).and(member.pushToken.isNotNull)) |             .and(pushToken.deviceType.eq(container)) | ||||||
|  |             .or(member.id.eq(4)) | ||||||
|  |  | ||||||
|         if (isAuth) { |         if (isAuth) { | ||||||
|             where = where.and(auth.isNotNull) |             where = where.and(auth.isNotNull) | ||||||
| @@ -164,11 +157,12 @@ class MemberQueryRepositoryImpl( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return queryFactory |         return queryFactory | ||||||
|             .select(creatorFollowing.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(creatorFollowing) |             .from(creatorFollowing) | ||||||
|             .innerJoin(creatorFollowing.creator, creator) |             .innerJoin(creatorFollowing.creator, creator) | ||||||
|             .innerJoin(creatorFollowing.member, member) |             .innerJoin(creatorFollowing.member, member) | ||||||
|             .innerJoin(creatorFollowing.member.notification, memberNotification) |             .innerJoin(creatorFollowing.member.notification, memberNotification) | ||||||
|  |             .innerJoin(pushToken).on(creatorFollowing.member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(creatorFollowing.member.auth, auth) |             .leftJoin(creatorFollowing.member.auth, auth) | ||||||
|             .where(where) |             .where(where) | ||||||
|             .fetch() |             .fetch() | ||||||
| @@ -187,18 +181,18 @@ class MemberQueryRepositoryImpl( | |||||||
|         val creator = QMember.member |         val creator = QMember.member | ||||||
|  |  | ||||||
|         var where = creatorFollowing.isActive.isTrue |         var where = creatorFollowing.isActive.isTrue | ||||||
|  |             .and(creatorFollowing.member.isActive.isTrue) | ||||||
|             .and(creatorFollowing.creator.id.eq(creatorId)) |             .and(creatorFollowing.creator.id.eq(creatorId)) | ||||||
|             .and(creatorFollowing.member.notification.live.isTrue) |             .and(creatorFollowing.member.notification.live.isTrue) | ||||||
|             .and(creatorFollowing.member.container.eq(container)) |  | ||||||
|             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) |             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) | ||||||
|             .and( |             .and( | ||||||
|                 creatorFollowing.member.id.notIn( |                 creatorFollowing.member.id.notIn( | ||||||
|                     blockMemberRepository.getBlockedMemberIdList(creatorId) |                     blockMemberRepository.getBlockedMemberIdList(creatorId) | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .and(creatorFollowing.member.pushToken.isNotNull) |  | ||||||
|             .and(creatorFollowing.isNotify.isTrue) |             .and(creatorFollowing.isNotify.isTrue) | ||||||
|             .or(creatorFollowing.member.id.eq(4).and(member.pushToken.isNotNull)) |             .and(pushToken.deviceType.eq(container)) | ||||||
|  |             .or(creatorFollowing.member.id.eq(4)) | ||||||
|  |  | ||||||
|         if (isAuth) { |         if (isAuth) { | ||||||
|             where = where.and(auth.isNotNull) |             where = where.and(auth.isNotNull) | ||||||
| @@ -209,36 +203,37 @@ class MemberQueryRepositoryImpl( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         val followingMemberPushToken = queryFactory |         val followingMemberPushToken = queryFactory | ||||||
|             .select(creatorFollowing.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(creatorFollowing) |             .from(creatorFollowing) | ||||||
|             .innerJoin(creatorFollowing.creator, creator) |             .innerJoin(creatorFollowing.creator, creator) | ||||||
|             .innerJoin(creatorFollowing.member, member) |             .innerJoin(creatorFollowing.member, member) | ||||||
|             .innerJoin(creatorFollowing.member.notification, memberNotification) |             .innerJoin(creatorFollowing.member.notification, memberNotification) | ||||||
|  |             .innerJoin(pushToken).on(creatorFollowing.member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(creatorFollowing.member.auth, auth) |             .leftJoin(creatorFollowing.member.auth, auth) | ||||||
|             .where(where) |             .where(where) | ||||||
|             .fetch() |             .fetch() | ||||||
|  |  | ||||||
|         where = liveReservation.isActive.isTrue |         where = liveReservation.isActive.isTrue | ||||||
|             .and(liveReservation.member.notification.live.isTrue) |             .and(liveReservation.member.notification.live.isTrue) | ||||||
|             .and(liveReservation.member.container.eq(container)) |  | ||||||
|             .and(liveReservation.member.email.notIn("admin@sodalive.net")) |             .and(liveReservation.member.email.notIn("admin@sodalive.net")) | ||||||
|             .and(liveReservation.member.pushToken.isNotNull) |  | ||||||
|             .and(liveReservation.room.id.eq(roomId)) |             .and(liveReservation.room.id.eq(roomId)) | ||||||
|             .and( |             .and( | ||||||
|                 liveReservation.member.id.notIn( |                 liveReservation.member.id.notIn( | ||||||
|                     blockMemberRepository.getBlockedMemberIdList(creatorId) |                     blockMemberRepository.getBlockedMemberIdList(creatorId) | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|  |             .and(pushToken.deviceType.eq(container)) | ||||||
|  |  | ||||||
|         if (isAuth) { |         if (isAuth) { | ||||||
|             where = where.and(auth.isNotNull) |             where = where.and(auth.isNotNull) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         val reservationMemberPushToken = queryFactory |         val reservationMemberPushToken = queryFactory | ||||||
|             .select(liveReservation.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(liveReservation) |             .from(liveReservation) | ||||||
|             .innerJoin(liveReservation.member, member) |             .innerJoin(liveReservation.member, member) | ||||||
|             .innerJoin(liveReservation.member.notification, memberNotification) |             .innerJoin(liveReservation.member.notification, memberNotification) | ||||||
|  |             .innerJoin(pushToken).on(liveReservation.member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(liveReservation.member.auth, auth) |             .leftJoin(liveReservation.member.auth, auth) | ||||||
|             .where(where) |             .where(where) | ||||||
|             .fetch() |             .fetch() | ||||||
| @@ -259,27 +254,27 @@ class MemberQueryRepositoryImpl( | |||||||
|         var where = creatorFollowing.isActive.isTrue |         var where = creatorFollowing.isActive.isTrue | ||||||
|             .and(creatorFollowing.creator.id.eq(creatorId)) |             .and(creatorFollowing.creator.id.eq(creatorId)) | ||||||
|             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) |             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) | ||||||
|             .and(creatorFollowing.member.container.eq(container)) |  | ||||||
|             .and(creatorFollowing.member.notification.uploadContent.isTrue) |             .and(creatorFollowing.member.notification.uploadContent.isTrue) | ||||||
|             .and( |             .and( | ||||||
|                 creatorFollowing.member.id.notIn( |                 creatorFollowing.member.id.notIn( | ||||||
|                     blockMemberRepository.getBlockedMemberIdList(creatorId) |                     blockMemberRepository.getBlockedMemberIdList(creatorId) | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .and(creatorFollowing.member.pushToken.isNotNull) |  | ||||||
|             .and(creatorFollowing.isNotify.isTrue) |             .and(creatorFollowing.isNotify.isTrue) | ||||||
|             .or(member.id.eq(4).and(member.pushToken.isNotNull)) |             .and(pushToken.deviceType.eq(container)) | ||||||
|  |             .or(member.id.eq(4)) | ||||||
|  |  | ||||||
|         if (isAuth) { |         if (isAuth) { | ||||||
|             where = where.and(auth.isNotNull) |             where = where.and(auth.isNotNull) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return queryFactory |         return queryFactory | ||||||
|             .select(creatorFollowing.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(creatorFollowing) |             .from(creatorFollowing) | ||||||
|             .innerJoin(creatorFollowing.creator, creator) |             .innerJoin(creatorFollowing.creator, creator) | ||||||
|             .innerJoin(creatorFollowing.member, member) |             .innerJoin(creatorFollowing.member, member) | ||||||
|             .innerJoin(member.notification, memberNotification) |             .innerJoin(member.notification, memberNotification) | ||||||
|  |             .innerJoin(pushToken).on(creatorFollowing.member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(member.auth, auth) |             .leftJoin(member.auth, auth) | ||||||
|             .where(where) |             .where(where) | ||||||
|             .fetch() |             .fetch() | ||||||
| @@ -291,16 +286,16 @@ class MemberQueryRepositoryImpl( | |||||||
|         return queryFactory |         return queryFactory | ||||||
|             .select( |             .select( | ||||||
|                 QGetMessageRecipientPushTokenResponse( |                 QGetMessageRecipientPushTokenResponse( | ||||||
|                     member.pushToken, |                     pushToken.token, | ||||||
|                     member.container |                     pushToken.deviceType | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .from(message) |             .from(message) | ||||||
|             .innerJoin(message.recipient, member) |             .innerJoin(message.recipient, member) | ||||||
|             .innerJoin(member.notification, memberNotification) |             .innerJoin(member.notification, memberNotification) | ||||||
|  |             .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|             .where( |             .where( | ||||||
|                 message.id.eq(messageId) |                 message.id.eq(messageId) | ||||||
|                     .and(member.pushToken.isNotNull) |  | ||||||
|                     .and(memberNotification.message.isTrue) |                     .and(memberNotification.message.isTrue) | ||||||
|             ) |             ) | ||||||
|             .fetchFirst() |             .fetchFirst() | ||||||
| @@ -313,7 +308,6 @@ class MemberQueryRepositoryImpl( | |||||||
|         var where = member.isActive.isTrue |         var where = member.isActive.isTrue | ||||||
|             .and(member.email.notIn("admin@sodalive.net")) |             .and(member.email.notIn("admin@sodalive.net")) | ||||||
|             .and(member.id.`in`(*recipients.toTypedArray())) |             .and(member.id.`in`(*recipients.toTypedArray())) | ||||||
|             .and(member.pushToken.isNotNull) |  | ||||||
|  |  | ||||||
|         if (isAuth != null) { |         if (isAuth != null) { | ||||||
|             where = if (isAuth) { |             where = if (isAuth) { | ||||||
| @@ -324,19 +318,21 @@ class MemberQueryRepositoryImpl( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         val aosPushTokens = queryFactory |         val aosPushTokens = queryFactory | ||||||
|             .select(member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(member) |             .from(member) | ||||||
|  |             .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(member.auth, auth) |             .leftJoin(member.auth, auth) | ||||||
|             .where(where.and(member.container.eq("aos"))) |             .where(where.and(pushToken.deviceType.eq("aos"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
|  |  | ||||||
|         val iosPushTokens = queryFactory |         val iosPushTokens = queryFactory | ||||||
|             .select(member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(member) |             .from(member) | ||||||
|  |             .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(member.auth, auth) |             .leftJoin(member.auth, auth) | ||||||
|             .where(where.and(member.container.eq("ios"))) |             .where(where.and(pushToken.deviceType.eq("ios"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
| @@ -373,6 +369,7 @@ class MemberQueryRepositoryImpl( | |||||||
|         val creator = QMember.member |         val creator = QMember.member | ||||||
|  |  | ||||||
|         val where = creatorFollowing.isActive.isTrue |         val where = creatorFollowing.isActive.isTrue | ||||||
|  |             .and(creatorFollowing.member.isActive.isTrue) | ||||||
|             .and(creatorFollowing.creator.id.eq(creatorId)) |             .and(creatorFollowing.creator.id.eq(creatorId)) | ||||||
|             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) |             .and(creatorFollowing.member.email.notIn("admin@sodalive.net")) | ||||||
|             .and( |             .and( | ||||||
| @@ -380,25 +377,26 @@ class MemberQueryRepositoryImpl( | |||||||
|                     blockMemberRepository.getBlockedMemberIdList(creatorId) |                     blockMemberRepository.getBlockedMemberIdList(creatorId) | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             .and(creatorFollowing.member.pushToken.isNotNull) |  | ||||||
|             .and(creatorFollowing.isNotify.isTrue) |             .and(creatorFollowing.isNotify.isTrue) | ||||||
|  |  | ||||||
|         val aosPushTokens = queryFactory |         val aosPushTokens = queryFactory | ||||||
|             .select(creatorFollowing.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(creatorFollowing) |             .from(creatorFollowing) | ||||||
|             .innerJoin(creatorFollowing.creator, creator) |             .innerJoin(creatorFollowing.creator, creator) | ||||||
|             .innerJoin(creatorFollowing.member, member) |             .innerJoin(creatorFollowing.member, member) | ||||||
|             .where(where.and(member.container.eq("aos"))) |             .innerJoin(pushToken).on(creatorFollowing.member.id.eq(pushToken.member.id)) | ||||||
|  |             .where(where.and(pushToken.deviceType.eq("aos"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
|  |  | ||||||
|         val iosPushTokens = queryFactory |         val iosPushTokens = queryFactory | ||||||
|             .select(creatorFollowing.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(creatorFollowing) |             .from(creatorFollowing) | ||||||
|             .innerJoin(creatorFollowing.creator, creator) |             .innerJoin(creatorFollowing.creator, creator) | ||||||
|             .innerJoin(creatorFollowing.member, member) |             .innerJoin(creatorFollowing.member, member) | ||||||
|             .where(where.and(member.container.eq("ios"))) |             .innerJoin(pushToken).on(creatorFollowing.member.id.eq(pushToken.member.id)) | ||||||
|  |             .where(where.and(pushToken.deviceType.eq("ios"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
| @@ -411,21 +409,22 @@ class MemberQueryRepositoryImpl( | |||||||
|             .and(liveReservation.isActive.isTrue) |             .and(liveReservation.isActive.isTrue) | ||||||
|  |  | ||||||
|         val aosPushTokens = queryFactory |         val aosPushTokens = queryFactory | ||||||
|             .select(liveReservation.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(liveReservation) |             .from(liveReservation) | ||||||
|             .innerJoin(liveReservation.room, liveRoom) |             .innerJoin(liveReservation.room, liveRoom) | ||||||
|             .innerJoin(liveReservation.member, member) |             .innerJoin(liveReservation.member, member) | ||||||
|             .where(where.and(member.container.eq("aos"))) |             .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|  |             .where(where.and(pushToken.deviceType.eq("aos"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
|  |  | ||||||
|         val iosPushTokens = queryFactory |         val iosPushTokens = queryFactory | ||||||
|             .select(liveReservation.member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(liveReservation) |             .from(liveReservation) | ||||||
|             .innerJoin(liveReservation.room, liveRoom) |             .innerJoin(liveReservation.room, liveRoom) | ||||||
|             .innerJoin(liveReservation.member, member) |             .innerJoin(liveReservation.member, member) | ||||||
|             .where(where.and(member.container.eq("ios"))) |             .where(where.and(pushToken.deviceType.eq("aos"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
| @@ -435,28 +434,29 @@ class MemberQueryRepositoryImpl( | |||||||
|  |  | ||||||
|     override fun getAuditionNoticeRecipientPushTokens(isAuth: Boolean): Map<String, List<List<String>>> { |     override fun getAuditionNoticeRecipientPushTokens(isAuth: Boolean): Map<String, List<List<String>>> { | ||||||
|         var where = memberNotification.audition.isTrue |         var where = memberNotification.audition.isTrue | ||||||
|             .and(member.pushToken.isNotNull) |  | ||||||
|  |  | ||||||
|         if (isAuth) { |         if (isAuth) { | ||||||
|             where = where.and(auth.isNotNull) |             where = where.and(auth.isNotNull) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         val aosPushTokens = queryFactory |         val aosPushTokens = queryFactory | ||||||
|             .select(member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(member) |             .from(member) | ||||||
|  |             .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(member.auth, auth) |             .leftJoin(member.auth, auth) | ||||||
|             .leftJoin(memberNotification).on(memberNotification.member.id.eq(member.id)) |             .leftJoin(memberNotification).on(memberNotification.member.id.eq(member.id)) | ||||||
|             .where(where.and(member.container.eq("aos"))) |             .where(where.and(pushToken.deviceType.eq("aos"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
|  |  | ||||||
|         val iosPushTokens = queryFactory |         val iosPushTokens = queryFactory | ||||||
|             .select(member.pushToken) |             .select(pushToken.token) | ||||||
|             .from(member) |             .from(member) | ||||||
|  |             .innerJoin(pushToken).on(member.id.eq(pushToken.member.id)) | ||||||
|             .leftJoin(member.auth, auth) |             .leftJoin(member.auth, auth) | ||||||
|             .leftJoin(memberNotification).on(memberNotification.member.id.eq(member.id)) |             .leftJoin(memberNotification).on(memberNotification.member.id.eq(member.id)) | ||||||
|             .where(where.and(member.container.eq("ios"))) |             .where(where.and(pushToken.deviceType.eq("ios"))) | ||||||
|             .fetch() |             .fetch() | ||||||
|             .toSet() |             .toSet() | ||||||
|             .chunked(500) |             .chunked(500) | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.common.ApiResponse | |||||||
| import kr.co.vividnext.sodalive.common.SodaException | import kr.co.vividnext.sodalive.common.SodaException | ||||||
| import kr.co.vividnext.sodalive.content.order.OrderService | import kr.co.vividnext.sodalive.content.order.OrderService | ||||||
| import kr.co.vividnext.sodalive.email.SendEmailService | import kr.co.vividnext.sodalive.email.SendEmailService | ||||||
|  | import kr.co.vividnext.sodalive.fcm.PushTokenService | ||||||
| import kr.co.vividnext.sodalive.jwt.TokenProvider | import kr.co.vividnext.sodalive.jwt.TokenProvider | ||||||
| import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository | import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository | ||||||
| import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser | import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser | ||||||
| @@ -82,6 +83,7 @@ class MemberService( | |||||||
|  |  | ||||||
|     private val orderService: OrderService, |     private val orderService: OrderService, | ||||||
|     private val emailService: SendEmailService, |     private val emailService: SendEmailService, | ||||||
|  |     private val pushTokenService: PushTokenService, | ||||||
|     private val canPaymentService: CanPaymentService, |     private val canPaymentService: CanPaymentService, | ||||||
|     private val nicknameGenerateService: NicknameGenerateService, |     private val nicknameGenerateService: NicknameGenerateService, | ||||||
|     private val memberNotificationService: MemberNotificationService, |     private val memberNotificationService: MemberNotificationService, | ||||||
| @@ -127,7 +129,6 @@ class MemberService( | |||||||
|             gender = Gender.NONE, |             gender = Gender.NONE, | ||||||
|             container = request.container |             container = request.container | ||||||
|         ) |         ) | ||||||
|         member.pushToken = request.pushToken |  | ||||||
|  |  | ||||||
|         if (!request.marketingPid.isNullOrBlank()) { |         if (!request.marketingPid.isNullOrBlank()) { | ||||||
|             member.activePid = request.marketingPid |             member.activePid = request.marketingPid | ||||||
| @@ -137,6 +138,14 @@ class MemberService( | |||||||
|         repository.save(member) |         repository.save(member) | ||||||
|         agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy) |         agreeTermsOfServiceAndPrivacyPolicy(member, stipulationTermsOfService, stipulationPrivacyPolicy) | ||||||
|  |  | ||||||
|  |         if (request.pushToken != null) { | ||||||
|  |             pushTokenService.registerToken( | ||||||
|  |                 memberId = member.id!!, | ||||||
|  |                 token = request.pushToken, | ||||||
|  |                 deviceType = request.container | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return SignUpResponse( |         return SignUpResponse( | ||||||
|             memberId = member.id!!, |             memberId = member.id!!, | ||||||
|             marketingPid = request.marketingPid, |             marketingPid = request.marketingPid, | ||||||
| @@ -235,16 +244,11 @@ class MemberService( | |||||||
|  |  | ||||||
|     @Transactional |     @Transactional | ||||||
|     fun updatePushToken(memberId: Long, pushToken: String, container: String) { |     fun updatePushToken(memberId: Long, pushToken: String, container: String) { | ||||||
|         val existsHavePushTokenMemberList = repository.findByPushToken(pushToken = pushToken) |         pushTokenService.registerToken( | ||||||
|         for (existsHavePushTokenMember in existsHavePushTokenMemberList) { |             memberId = memberId, | ||||||
|             existsHavePushTokenMember.pushToken = null |             token = pushToken, | ||||||
|         } |             deviceType = container | ||||||
|  |         ) | ||||||
|         val member = repository.findByIdOrNull(id = memberId) |  | ||||||
|             ?: throw SodaException("로그인 정보를 확인해주세요.") |  | ||||||
|  |  | ||||||
|         member.pushToken = pushToken |  | ||||||
|         member.container = container |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional |     @Transactional | ||||||
| @@ -551,6 +555,7 @@ class MemberService( | |||||||
|             ?: throw SodaException("로그인 정보를 확인해주세요.") |             ?: throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|  |  | ||||||
|         member.pushToken = null |         member.pushToken = null | ||||||
|  |         pushTokenService.logout(memberId = memberId) | ||||||
|  |  | ||||||
|         val lock = getOrCreateLock(memberId = memberId) |         val lock = getOrCreateLock(memberId = memberId) | ||||||
|         lock.write { |         lock.write { | ||||||
| @@ -568,6 +573,7 @@ class MemberService( | |||||||
|             ?: throw SodaException("로그인 정보를 확인해주세요.") |             ?: throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|  |  | ||||||
|         member.pushToken = null |         member.pushToken = null | ||||||
|  |         pushTokenService.logout(memberId = memberId) | ||||||
|  |  | ||||||
|         val lock = getOrCreateLock(memberId = memberId) |         val lock = getOrCreateLock(memberId = memberId) | ||||||
|         lock.write { tokenRepository.deleteById(memberId) } |         lock.write { tokenRepository.deleteById(memberId) } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user