Compare commits

..

3 Commits

8 changed files with 192 additions and 67 deletions

View File

@ -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()
} }

View File

@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.creator.admin.member
import kr.co.vividnext.sodalive.common.ApiResponse 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.fcm.PushTokenService
import kr.co.vividnext.sodalive.jwt.TokenProvider import kr.co.vividnext.sodalive.jwt.TokenProvider
import kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.member.MemberRepository
import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.MemberRole
@ -25,6 +26,8 @@ class CreatorAdminMemberService(
private val tokenProvider: TokenProvider, private val tokenProvider: TokenProvider,
private val authenticationManagerBuilder: AuthenticationManagerBuilder, private val authenticationManagerBuilder: AuthenticationManagerBuilder,
private val pushTokenService: PushTokenService,
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String private val cloudFrontHost: String
) { ) {
@ -44,6 +47,7 @@ class CreatorAdminMemberService(
?: 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 {

View File

@ -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
} }

View 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
}

View File

@ -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()
}
}

View File

@ -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)
}
}
}

View File

@ -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)

View File

@ -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) }
@ -824,7 +830,14 @@ class MemberService(
provider = MemberProvider.GOOGLE, provider = MemberProvider.GOOGLE,
container = container container = container
) )
member.pushToken = pushToken
if (pushToken != null) {
pushTokenService.registerToken(
memberId = member.id!!,
token = pushToken,
deviceType = container
)
}
if (!marketingPid.isNullOrBlank()) { if (!marketingPid.isNullOrBlank()) {
member.activePid = marketingPid member.activePid = marketingPid
@ -873,7 +886,14 @@ class MemberService(
provider = MemberProvider.KAKAO, provider = MemberProvider.KAKAO,
container = container container = container
) )
member.pushToken = pushToken
if (pushToken != null) {
pushTokenService.registerToken(
memberId = member.id!!,
token = pushToken,
deviceType = container
)
}
if (!marketingPid.isNullOrBlank()) { if (!marketingPid.isNullOrBlank()) {
member.activePid = marketingPid member.activePid = marketingPid