Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,9 @@ class GatheringMemberService(

return gatheringMembers.map { it.memberId }
}

@Transactional
fun deleteGatheringMember(memberId: Long) {
gatheringMemberRepository.deleteAllByMemberId(memberId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ class GatheringProposalService(
pushService.sendPush(
PushCommand.Push.Proposal(
pushType = PushType.PROPOSAL,
gatheringId = command.gatheringId
gatheringId = command.gatheringId,
proposalId = command.proposalId,
proposerMemberId = command.memberId,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import nexters.tuk.application.gathering.dto.request.GatheringCommand
import nexters.tuk.application.gathering.dto.response.GatheringResponse
import nexters.tuk.domain.gathering.Gathering
import nexters.tuk.domain.gathering.GatheringRepository
import nexters.tuk.domain.gathering.findByIdOrThrow
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class GatheringGenerateService(
class GatheringService(
private val gatheringRepository: GatheringRepository,
private val gatheringMemberService: GatheringMemberService,
private val gatheringTagService: GatheringTagService,
Expand All @@ -32,4 +33,16 @@ class GatheringGenerateService(
gathering.id,
)
}

@Transactional
fun updateGathering(command: GatheringCommand.Update): GatheringResponse.Simple {
val gathering = gatheringRepository.findByIdOrThrow(command.gatheringId)
gathering.update(command)

return GatheringResponse.Simple(
gatheringId = gathering.id,
gatheringName = gathering.name,
intervalDays = gathering.intervalDays,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package nexters.tuk.application.gathering.handler

import nexters.tuk.application.gathering.GatheringMemberService
import nexters.tuk.application.member.MemberService
import nexters.tuk.application.member.event.MemberEvent
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Component
import org.springframework.transaction.event.TransactionPhase
import org.springframework.transaction.event.TransactionalEventListener

@Component
class GatheringHandler(
private val gatheringMemberService: GatheringMemberService,
) {

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
fun handleDeleteMember(event: MemberEvent.MemberDeleted) {
gatheringMemberService.deleteGatheringMember(event.memberId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ package nexters.tuk.application.member

import nexters.tuk.application.member.dto.request.MemberCommand
import nexters.tuk.application.member.dto.response.MemberResponse
import nexters.tuk.application.member.event.MemberEvent
import nexters.tuk.contract.ApiResponse
import nexters.tuk.contract.BaseException
import nexters.tuk.contract.ErrorType
import nexters.tuk.domain.member.Member
import nexters.tuk.domain.member.MemberRepository
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class MemberService(
private val memberRepository: MemberRepository,
private val applicationEventPublisher: ApplicationEventPublisher,
) {
@Transactional
fun login(command: MemberCommand.Login): MemberResponse.Login {
Expand Down Expand Up @@ -48,6 +51,7 @@ class MemberService(
@Transactional
fun deleteMember(memberId: Long) {
memberRepository.leave(memberId)
applicationEventPublisher.publishEvent(MemberEvent.MemberDeleted(memberId))
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nexters.tuk.application.member.event

class MemberEvent {
data class MemberDeleted(
val memberId: Long,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import nexters.tuk.application.proposal.vo.ProposalPurposeInfo
class ProposalCommand {
data class Propose(
val memberId: Long,
val gatheringId: Long?,
val gatheringId: Long? = null,
val purpose: ProposalPurposeInfo,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package nexters.tuk.application.push

enum class PushDeepLink(
val link: String,
) {
DEFAULT("tuk-app://tuk"),
PROPOSAL("tuk-app://tuk/proposal-detail/%s"),

;

fun getLink(proposalId: Long): String {
return this.link.format(proposalId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,18 @@ class PushService(

@Transactional
fun sendPush(command: PushCommand.Push) {
val pushMessage = PushMessage.random()
when (command) {
is PushCommand.Push.GatheringNotification -> {
val memberIds = command.recipients.map { it.memberId }
val pushMessage = PushMessage.random(pushType = command.pushType)
val memberIds = gatheringMemberService.getGatheringMemberIds(gatheringId = command.gatheringId)
pushAll(memberIds = memberIds, pushMessage = pushMessage)
logger.info("Sent gathering notification push. Recipients: ${command.recipients.size}, PushType: ${command.pushType}")
logger.info("Sent gathering notification push. Recipients: ${memberIds.size}, PushType: ${command.pushType}")
}

is PushCommand.Push.Proposal -> {
val pushMessage = PushMessage.random(pushType = command.pushType, proposalId = command.proposalId)
val memberIds = gatheringMemberService.getGatheringMemberIds(gatheringId = command.gatheringId)
.filter { memberId -> memberId != command.proposerMemberId }
pushAll(memberIds = memberIds, pushMessage = pushMessage)
logger.info("Sent proposal push. GatheringId: ${command.gatheringId}, Recipients: ${memberIds.size}, PushType: ${command.pushType}")
}
Expand All @@ -37,7 +39,7 @@ class PushService(

private fun pushAll(
memberIds: List<Long>,
pushMessage: PushMessage,
pushMessage: PushData,
) {
logger.info("PushService.pushAll -> memberIds: $memberIds")
val memberNameMap = memberService.getMembers(memberIds).associate { it.memberId to it.memberName }
Expand All @@ -50,7 +52,8 @@ class PushService(
memberNameMap[token.memberId]
?: return@forEach
),
body = pushMessage.body
body = pushMessage.body,
deepLink = pushMessage.deepLink,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,84 @@
package nexters.tuk.application.push

import nexters.tuk.contract.push.PushType

enum class PushMessage(
val pushType: PushType,
private val title: String,
val body: String,
val deepLink: PushDeepLink,
) {
PUSH_VERSION_A("%s님! 방금 '툭'— 누군가 만남을 제안했어요.", "슬슬 그리워질 타이밍... 아닐까요?"),
PUSH_VERSION_B("누군가 %s님을 떠올리며 툭— 건넸어요.", "이번엔 그냥 지나치지 마세요 :)"),
PUSH_VERSION_C("%s님, 툭— 누가 당신을 부르고 있어요.", "이번엔 누굴까? 살짝 들여다볼래요?"),
PUSH_VERSION_D("%s님, 누가 몰래 '툭' 했대요.", "그냥 넘어가긴... 좀 아쉽죠?"),
PUSH_VERSION_A(
PushType.GATHERING_NOTIFICATION,
"%s님! 방금 '툭'— 누군가 만남을 제안했어요.",
"슬슬 그리워질 타이밍... 아닐까요?",
PushDeepLink.DEFAULT,
),
PUSH_VERSION_B(
PushType.GATHERING_NOTIFICATION,
"누군가 %s님을 떠올리며 툭— 건넸어요.",
"이번엔 그냥 지나치지 마세요 :)",
PushDeepLink.DEFAULT,
),
PUSH_VERSION_C(
PushType.GATHERING_NOTIFICATION,
"%s님, 툭— 누가 당신을 부르고 있어요.",
"이번엔 누굴까? 살짝 들여다볼래요?",
PushDeepLink.DEFAULT,
),
PUSH_VERSION_D(
PushType.GATHERING_NOTIFICATION,
"%s님, 누가 몰래 '툭' 했대요.",
"그냥 넘어가긴... 좀 아쉽죠?",
PushDeepLink.DEFAULT,
),

PROPOSAL(
PushType.PROPOSAL,
"%s님! 방금 '툭'— 누군가 만남을 제안했어요.",
"슬슬 그리워질 타이밍... 아닐까요?",
PushDeepLink.PROPOSAL,
)
;


fun getTitle(memberName: String): String {
return title.format(memberName)
}

companion object {
fun random(): PushMessage {
return entries.toTypedArray().random()
fun random(pushType: PushType, proposalId: Long? = null): PushData {
return when (pushType) {
PushType.GATHERING_NOTIFICATION -> {
val randomMessage = entries.filter { it.pushType == PushType.GATHERING_NOTIFICATION }.random()
PushData(randomMessage, null)
}

PushType.PROPOSAL -> {
require(proposalId != null) { "초대장 푸시는 proposalId가 필수입니다." }
val randomMessage = entries.filter { it.pushType == PushType.PROPOSAL }.random()
PushData(randomMessage, Meta(proposalId))
}
}
}
}
}
}

data class PushData(
val message: PushMessage,
val meta: Meta? = null,
) {
fun getTitle(memberName: String): String {
return message.getTitle(memberName)
}

val body: String
get() = message.body

val deepLink: String
get() = message.deepLink.link
}

data class Meta(
val proposalId: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,22 @@ class PushCommand {

@Schema(title = "모임 정기 푸시 (GATHERING_NOTIFICATION)")
data class GatheringNotification(
val recipients: List<PushRecipient>,
val gatheringId: Long,
override val pushType: PushType = PushType.GATHERING_NOTIFICATION,
) : Push

@Schema(title = "초대장 푸시 (PROPOSAL)")
data class Proposal(
override val pushType: PushType = PushType.PROPOSAL,
val gatheringId: Long,
val proposalId: Long,
val proposerMemberId: Long,
) : Push
}

data class MessagePayload(
val title: String,
val body: String,
)

data class PushRecipient(
val memberId: Long,
val deepLink: String,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nexters.tuk.domain.gathering

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param

Expand All @@ -23,4 +24,13 @@ interface GatheringMemberRepository : JpaRepository<GatheringMember, Long> {


fun findAllByGathering(gathering: Gathering): List<GatheringMember>

@Modifying
@Query(
"""
DELETE FROM GatheringMember gm
WHERE gm.memberId = :memberId
"""
)
fun deleteAllByMemberId(memberId: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ class FcmPushSender : PushSender {
Notification.builder()
.setTitle(message.title)
.setBody(message.body)
// .setImage(message.imageUrl) // TODO: 푸시 이미지 확인 필요
.build()
)
.putData("deepLink", message.deepLink)
.build()
}

Expand All @@ -216,9 +216,9 @@ class FcmPushSender : PushSender {
Notification.builder()
.setTitle(message.title)
.setBody(message.body)
// .setImage(message.imageUrl) // TODO: 푸시 이미지 확인 필요
.build()
)
.putData("deepLink", message.deepLink)
.build()
}
}
Loading
Loading