diff --git a/internal/bossraid/model.go b/internal/bossraid/model.go index 1d50e79..80fe332 100644 --- a/internal/bossraid/model.go +++ b/internal/bossraid/model.go @@ -12,7 +12,8 @@ const ( StatusWaiting RoomStatus = "waiting" StatusInProgress RoomStatus = "in_progress" StatusCompleted RoomStatus = "completed" - StatusFailed RoomStatus = "failed" + StatusFailed RoomStatus = "failed" + StatusRewardFailed RoomStatus = "reward_failed" ) // BossRoom represents a boss raid session room. diff --git a/internal/bossraid/repository.go b/internal/bossraid/repository.go index a7809f0..5369411 100644 --- a/internal/bossraid/repository.go +++ b/internal/bossraid/repository.go @@ -219,6 +219,17 @@ func (r *Repository) ResetStaleSlots(threshold time.Time) (int64, error) { return result.RowsAffected, result.Error } +// UpdateRoomStatus updates only the status of a boss room by session name. +func (r *Repository) UpdateRoomStatus(sessionName string, status RoomStatus) error { + result := r.db.Model(&BossRoom{}). + Where("session_name = ?", sessionName). + Update("status", status) + if result.RowsAffected == 0 { + return fmt.Errorf("방을 찾을 수 없습니다: %s", sessionName) + } + return result.Error +} + // GetRoomSlotsByServer returns all room slots for a given server. func (r *Repository) GetRoomSlotsByServer(serverID uint) ([]RoomSlot, error) { var slots []RoomSlot diff --git a/internal/bossraid/service.go b/internal/bossraid/service.go index f683b7c..bd25236 100644 --- a/internal/bossraid/service.go +++ b/internal/bossraid/service.go @@ -55,6 +55,9 @@ func (s *Service) SetExpGranter(fn func(username string, exp int) error) { // Allocates an idle room slot from a registered dedicated server. // Returns the room with assigned session name. func (s *Service) RequestEntry(usernames []string, bossID int) (*BossRoom, error) { + // 좀비 슬롯 정리 — idle 슬롯 검색 전에 stale 인스턴스를 리셋 + s.CheckStaleSlots() + if len(usernames) == 0 { return nil, fmt.Errorf("플레이어 목록이 비어있습니다") } @@ -222,6 +225,7 @@ func (s *Service) CompleteRaid(sessionName string, rewards []PlayerReward) (*Bos // Grant rewards outside the transaction to avoid holding the lock during RPC calls resultRewards = make([]RewardResult, 0, len(rewards)) + hasRewardFailure := false if s.rewardGrant != nil { for _, r := range rewards { grantErr := s.rewardGrant(r.Username, r.TokenAmount, r.Assets) @@ -229,11 +233,19 @@ func (s *Service) CompleteRaid(sessionName string, rewards []PlayerReward) (*Bos if grantErr != nil { result.Error = grantErr.Error() log.Printf("보상 지급 실패: %s: %v", r.Username, grantErr) + hasRewardFailure = true } resultRewards = append(resultRewards, result) } } + // 보상 실패가 있으면 상태를 reward_failed로 업데이트 + if hasRewardFailure { + if err := s.repo.UpdateRoomStatus(sessionName, StatusRewardFailed); err != nil { + log.Printf("보상 실패 상태 업데이트 실패: %s: %v", sessionName, err) + } + } + // Grant experience to players if s.expGrant != nil { for _, r := range rewards {