feat(chat-quota): 채팅방 쿼터 충전 방식과 옵션을 확장한다

This commit is contained in:
2026-04-29 18:44:36 +09:00
parent 0c0da6cbc9
commit d736ec4368
5 changed files with 422 additions and 14 deletions

View File

@@ -27,7 +27,9 @@ class ChatRoomQuotaController(
) {
data class PurchaseRoomQuotaRequest(
val container: String
val container: String,
val chargeType: ChatRoomQuotaChargeType = ChatRoomQuotaChargeType.CAN,
val canOption: ChatRoomQuotaCanOption? = null
)
data class PurchaseRoomQuotaResponse(
@@ -45,8 +47,9 @@ class ChatRoomQuotaController(
/**
* 채팅방 유료 쿼터 구매 API
* - 참여 여부 검증(내가 USER로 참여 중인 활성 방)
* - 30캔 결제 (UseCan에 chatRoomId:characterId 기록)
* - 방 유료 쿼터 40 충전
* - 요청 DTO로 캔 충전 / 광고 충전을 구분
* - 캔 충전은 옵션별 캔 차감 후 방 유료 쿼터 지급
* - 광고 충전은 캔 차감 없이 방 유료 쿼터 5 지급
*/
@PostMapping("/{chatRoomId}/quota/purchase")
fun purchaseRoomQuota(
@@ -74,13 +77,28 @@ class ChatRoomQuotaController(
val characterId = character.id
?: throw SodaException(messageKey = "chat.room.quota.character_required")
val status = chatRoomQuotaService.purchase(
memberId = member.id!!,
chatRoomId = chatRoomId,
characterId = characterId,
addPaid = 12,
container = req.container
)
val chargeType = req.chargeType
val status = when (chargeType) {
ChatRoomQuotaChargeType.CAN -> {
val canOption = req.canOption ?: ChatRoomQuotaCanOption.CAN_10
chatRoomQuotaService.purchaseWithCan(
memberId = member.id!!,
chatRoomId = chatRoomId,
characterId = characterId,
canOption = canOption,
container = req.container
)
}
ChatRoomQuotaChargeType.AD -> {
chatRoomQuotaService.purchaseWithAd(
memberId = member.id!!,
chatRoomId = chatRoomId,
characterId = characterId
)
}
}
ApiResponse.ok(
PurchaseRoomQuotaResponse(

View File

@@ -0,0 +1,14 @@
package kr.co.vividnext.sodalive.chat.quota.room
enum class ChatRoomQuotaChargeType {
CAN,
AD
}
enum class ChatRoomQuotaCanOption(
val needCan: Int,
val quota: Int
) {
CAN_10(10, 15),
CAN_20(20, 40)
}

View File

@@ -13,6 +13,10 @@ class ChatRoomQuotaService(
private val repo: ChatRoomQuotaRepository,
private val canPaymentService: CanPaymentService
) {
companion object {
private const val AD_REWARD_QUOTA = 5
}
data class RoomQuotaStatus(
val totalRemaining: Int,
val nextRechargeAtEpochMillis: Long?,
@@ -122,23 +126,50 @@ class ChatRoomQuotaService(
}
@Transactional
fun purchase(
fun purchaseWithCan(
memberId: Long,
chatRoomId: Long,
characterId: Long,
addPaid: Int = 12,
canOption: ChatRoomQuotaCanOption,
container: String
): RoomQuotaStatus {
// 요구사항: 10캔 결제 및 UseCan에 방/캐릭터 기록
canPaymentService.spendCan(
memberId = memberId,
needCan = 10,
needCan = canOption.needCan,
canUsage = CanUsage.CHAT_QUOTA_PURCHASE,
chatRoomId = chatRoomId,
characterId = characterId,
container = container
)
return addPaidQuota(
memberId = memberId,
chatRoomId = chatRoomId,
characterId = characterId,
addPaid = canOption.quota
)
}
@Transactional
fun purchaseWithAd(
memberId: Long,
chatRoomId: Long,
characterId: Long
): RoomQuotaStatus {
return addPaidQuota(
memberId = memberId,
chatRoomId = chatRoomId,
characterId = characterId,
addPaid = AD_REWARD_QUOTA
)
}
private fun addPaidQuota(
memberId: Long,
chatRoomId: Long,
characterId: Long,
addPaid: Int
): RoomQuotaStatus {
val quota = repo.findForUpdate(memberId, chatRoomId) ?: repo.save(
ChatRoomQuota(
memberId = memberId,