package bossraid import ( "encoding/json" "fmt" "log" "time" "github.com/tolelom/tolchain/core" ) type Service struct { repo *Repository rewardGrant func(username string, tokenAmount uint64, assets []core.MintAssetPayload) error } func NewService(repo *Repository) *Service { return &Service{repo: repo} } // SetRewardGranter sets the callback for granting rewards via blockchain. func (s *Service) SetRewardGranter(fn func(username string, tokenAmount uint64, assets []core.MintAssetPayload) error) { s.rewardGrant = fn } // RequestEntry creates a new boss room for a party. // Returns the room with assigned session name. func (s *Service) RequestEntry(usernames []string, bossID int) (*BossRoom, error) { if len(usernames) == 0 { return nil, fmt.Errorf("플레이어 목록이 비어있습니다") } if len(usernames) > 3 { return nil, fmt.Errorf("최대 3명까지 입장할 수 있습니다") } // Check if any player is already in an active room for _, username := range usernames { count, err := s.repo.CountActiveByUsername(username) if err != nil { return nil, fmt.Errorf("플레이어 상태 확인 실패: %w", err) } if count > 0 { return nil, fmt.Errorf("플레이어 %s가 이미 보스 레이드 중입니다", username) } } playersJSON, err := json.Marshal(usernames) if err != nil { return nil, fmt.Errorf("플레이어 목록 직렬화 실패: %w", err) } sessionName := fmt.Sprintf("BossRaid_%d_%d", bossID, time.Now().UnixNano()) room := &BossRoom{ SessionName: sessionName, BossID: bossID, Status: StatusWaiting, MaxPlayers: 3, Players: string(playersJSON), } if err := s.repo.Create(room); err != nil { return nil, fmt.Errorf("방 생성 실패: %w", err) } return room, nil } // StartRaid marks a room as in_progress. func (s *Service) StartRaid(sessionName string) (*BossRoom, error) { room, err := s.repo.FindBySessionName(sessionName) if err != nil { return nil, fmt.Errorf("방을 찾을 수 없습니다: %w", err) } if room.Status != StatusWaiting { return nil, fmt.Errorf("시작할 수 없는 상태입니다: %s", room.Status) } now := time.Now() room.Status = StatusInProgress room.StartedAt = &now if err := s.repo.Update(room); err != nil { return nil, fmt.Errorf("상태 업데이트 실패: %w", err) } return room, nil } // PlayerReward describes the reward for a single player. type PlayerReward struct { Username string `json:"username"` TokenAmount uint64 `json:"tokenAmount"` Assets []core.MintAssetPayload `json:"assets"` } // RewardResult holds the result of granting a reward to one player. type RewardResult struct { Username string `json:"username"` Success bool `json:"success"` Error string `json:"error,omitempty"` } // CompleteRaid marks a room as completed and grants rewards via blockchain. func (s *Service) CompleteRaid(sessionName string, rewards []PlayerReward) (*BossRoom, []RewardResult, error) { room, err := s.repo.FindBySessionName(sessionName) if err != nil { return nil, nil, fmt.Errorf("방을 찾을 수 없습니다: %w", err) } if room.Status != StatusInProgress { return nil, nil, fmt.Errorf("완료할 수 없는 상태입니다: %s", room.Status) } // Validate reward recipients are room players var players []string if err := json.Unmarshal([]byte(room.Players), &players); err != nil { return nil, nil, fmt.Errorf("플레이어 목록 파싱 실패: %w", err) } playerSet := make(map[string]bool, len(players)) for _, p := range players { playerSet[p] = true } for _, r := range rewards { if !playerSet[r.Username] { return nil, nil, fmt.Errorf("보상 대상 %s가 방의 플레이어가 아닙니다", r.Username) } } // Mark room completed now := time.Now() room.Status = StatusCompleted room.CompletedAt = &now if err := s.repo.Update(room); err != nil { return nil, nil, fmt.Errorf("상태 업데이트 실패: %w", err) } // Grant rewards results := make([]RewardResult, 0, len(rewards)) if s.rewardGrant != nil { for _, r := range rewards { grantErr := s.rewardGrant(r.Username, r.TokenAmount, r.Assets) result := RewardResult{Username: r.Username, Success: grantErr == nil} if grantErr != nil { result.Error = grantErr.Error() log.Printf("보상 지급 실패: %s: %v", r.Username, grantErr) } results = append(results, result) } } return room, results, nil } // FailRaid marks a room as failed. func (s *Service) FailRaid(sessionName string) (*BossRoom, error) { room, err := s.repo.FindBySessionName(sessionName) if err != nil { return nil, fmt.Errorf("방을 찾을 수 없습니다: %w", err) } if room.Status != StatusWaiting && room.Status != StatusInProgress { return nil, fmt.Errorf("실패 처리할 수 없는 상태입니다: %s", room.Status) } now := time.Now() room.Status = StatusFailed room.CompletedAt = &now if err := s.repo.Update(room); err != nil { return nil, fmt.Errorf("상태 업데이트 실패: %w", err) } return room, nil } // GetRoom returns a room by session name. func (s *Service) GetRoom(sessionName string) (*BossRoom, error) { return s.repo.FindBySessionName(sessionName) }