Files
Erupe/server/channelserver/svc_tower.go
Houmgaor 7a56810e78 refactor(tower): extract tower logic into TowerService
The tower repo had business logic beyond simple CRUD: AddGem used a
fetch-transform-save pattern, progress capping was inline in the
handler, and RP donation orchestrated multiple repo calls with
conditional page advancement. Move these into a new TowerService
following the established service layer pattern.
2026-02-24 16:07:37 +01:00

103 lines
2.9 KiB
Go

package channelserver
import (
"erupe-ce/common/stringsupport"
"go.uber.org/zap"
)
// DonateRPResult holds the outcome of a guild tower RP donation.
type DonateRPResult struct {
ActualDonated uint16
Advanced bool
}
// TowerService encapsulates tower business logic, sitting between handlers and repos.
type TowerService struct {
towerRepo TowerRepo
logger *zap.Logger
}
// NewTowerService creates a new TowerService.
func NewTowerService(tr TowerRepo, log *zap.Logger) *TowerService {
return &TowerService{
towerRepo: tr,
logger: log,
}
}
// AddGem adds quantity to a specific gem index for a character.
// This is a fetch-transform-save operation that reads the current gems CSV,
// updates the value at the given index, and writes back.
func (svc *TowerService) AddGem(charID uint32, gemIndex int, quantity int) error {
gems, err := svc.towerRepo.GetGems(charID)
if err != nil {
return err
}
newGems := stringsupport.CSVSetIndex(gems, gemIndex, stringsupport.CSVGetIndex(gems, gemIndex)+quantity)
return svc.towerRepo.UpdateGems(charID, newGems)
}
// GetTenrouiraiProgressCapped returns the guild's tenrouirai progress with
// mission scores capped to their respective goals.
func (svc *TowerService) GetTenrouiraiProgressCapped(guildID uint32) (TenrouiraiProgressData, error) {
progress, err := svc.towerRepo.GetTenrouiraiProgress(guildID)
if err != nil {
return progress, err
}
if progress.Page < 1 {
progress.Page = 1
}
idx := int(progress.Page*3) - 3
if idx >= 0 && idx+2 < len(tenrouiraiData) {
if progress.Mission1 > tenrouiraiData[idx].Goal {
progress.Mission1 = tenrouiraiData[idx].Goal
}
if progress.Mission2 > tenrouiraiData[idx+1].Goal {
progress.Mission2 = tenrouiraiData[idx+1].Goal
}
if progress.Mission3 > tenrouiraiData[idx+2].Goal {
progress.Mission3 = tenrouiraiData[idx+2].Goal
}
}
return progress, nil
}
// DonateGuildTowerRP processes a tower RP donation, advancing the mission page
// if the cumulative donation meets the requirement. Returns the actual RP consumed
// and whether the page was advanced.
func (svc *TowerService) DonateGuildTowerRP(guildID uint32, donatedRP uint16) (*DonateRPResult, error) {
page, donated, err := svc.towerRepo.GetGuildTowerPageAndRP(guildID)
if err != nil {
return nil, err
}
var requirement int
for i := 0; i < (page*3)+1 && i < len(tenrouiraiData); i++ {
requirement += int(tenrouiraiData[i].Cost)
}
result := &DonateRPResult{
ActualDonated: donatedRP,
}
if donated+int(donatedRP) >= requirement {
if err := svc.towerRepo.AdvanceTenrouiraiPage(guildID); err != nil {
svc.logger.Error("Failed to advance tower mission page", zap.Error(err))
return nil, err
}
result.ActualDonated = uint16(requirement - donated)
result.Advanced = true
}
if err := svc.towerRepo.DonateGuildTowerRP(guildID, result.ActualDonated); err != nil {
svc.logger.Error("Failed to update guild tower RP", zap.Error(err))
return nil, err
}
return result, nil
}