refactor(channelserver): eliminate *sqlx.Rows/*sql.Rows from repository interfaces

Move scan loops from handlers into repository methods so that interfaces
return typed slices instead of leaking database cursors. This fixes
resource leaks (7 of 12 call sites never closed rows) and makes all
12 methods mockable for unit tests.

Affected repos: CafeRepo, ShopRepo, EventRepo, RengokuRepo, DivaRepo,
ScenarioRepo, MiscRepo, MercenaryRepo. New structs: DivaEvent,
MercenaryLoan, GuildHuntCatUsage. EventRepo.GetEventQuests left as-is
(requires broader Server refactor).
This commit is contained in:
Houmgaor
2026-02-21 14:16:58 +01:00
parent a9cca84bc3
commit 2be589beae
17 changed files with 203 additions and 178 deletions

View File

@@ -1,7 +1,8 @@
package channelserver
import (
"database/sql"
"fmt"
"time"
"github.com/jmoiron/sqlx"
)
@@ -30,21 +31,73 @@ func (r *MercenaryRepository) NextAirouID() (uint32, error) {
return id, err
}
// MercenaryLoan represents a character that has a pact with a rasta.
type MercenaryLoan struct {
Name string
CharID uint32
PactID int
}
// GetMercenaryLoans returns characters that have a pact with the given character's rasta_id.
func (r *MercenaryRepository) GetMercenaryLoans(charID uint32) (*sql.Rows, error) {
return r.db.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", charID)
func (r *MercenaryRepository) GetMercenaryLoans(charID uint32) ([]MercenaryLoan, error) {
rows, err := r.db.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", charID)
if err != nil {
return nil, fmt.Errorf("query mercenary loans: %w", err)
}
defer rows.Close()
var result []MercenaryLoan
for rows.Next() {
var l MercenaryLoan
if err := rows.Scan(&l.Name, &l.CharID, &l.PactID); err != nil {
return nil, fmt.Errorf("scan mercenary loan: %w", err)
}
result = append(result, l)
}
return result, rows.Err()
}
// GuildHuntCatUsage represents cats_used and start time from a guild hunt.
type GuildHuntCatUsage struct {
CatsUsed string
Start time.Time
}
// GetGuildHuntCatsUsed returns cats_used and start from guild_hunts for a given character.
func (r *MercenaryRepository) GetGuildHuntCatsUsed(charID uint32) (*sql.Rows, error) {
return r.db.Query(`SELECT cats_used, start FROM guild_hunts gh
func (r *MercenaryRepository) GetGuildHuntCatsUsed(charID uint32) ([]GuildHuntCatUsage, error) {
rows, err := r.db.Query(`SELECT cats_used, start FROM guild_hunts gh
INNER JOIN characters c ON gh.host_id = c.id WHERE c.id=$1`, charID)
if err != nil {
return nil, fmt.Errorf("query guild hunt cats: %w", err)
}
defer rows.Close()
var result []GuildHuntCatUsage
for rows.Next() {
var u GuildHuntCatUsage
if err := rows.Scan(&u.CatsUsed, &u.Start); err != nil {
return nil, fmt.Errorf("scan guild hunt cat: %w", err)
}
result = append(result, u)
}
return result, rows.Err()
}
// GetGuildAirou returns otomoairou data for all characters in a guild.
func (r *MercenaryRepository) GetGuildAirou(guildID uint32) (*sql.Rows, error) {
return r.db.Query(`SELECT c.otomoairou FROM characters c
func (r *MercenaryRepository) GetGuildAirou(guildID uint32) ([][]byte, error) {
rows, err := r.db.Query(`SELECT c.otomoairou FROM characters c
INNER JOIN guild_characters gc ON gc.character_id = c.id
WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL
ORDER BY c.id LIMIT 60`, guildID)
if err != nil {
return nil, fmt.Errorf("query guild airou: %w", err)
}
defer rows.Close()
var result [][]byte
for rows.Next() {
var data []byte
if err := rows.Scan(&data); err != nil {
return nil, fmt.Errorf("scan guild airou: %w", err)
}
result = append(result, data)
}
return result, rows.Err()
}