Files
Erupe/server/channelserver/repo_character.go
Houmgaor d642cbef24 refactor(channelserver): migrate remaining character queries to CharacterRepository
Add 18 new typed methods to CharacterRepository (ReadTime, SaveTime,
SaveInt, SaveBool, SaveString, ReadBool, ReadString, LoadColumnWithDefault,
SetDeleted, UpdateDailyCafe, ResetDailyQuests, ReadEtcPoints, ResetCafeTime,
UpdateGuildPostChecked, ReadGuildPostChecked, SaveMercenary, UpdateGCPAndPact,
FindByRastaID) and migrate ~56 inline SQL queries across 13 handler files.

Pure refactor — zero behavior change. Each handler produces identical SQL
with identical parameters. Cross-table JOINs and bulk CharacterSaveData
operations are intentionally left out of scope.
2026-02-20 21:57:24 +01:00

213 lines
7.9 KiB
Go

package channelserver
import (
"database/sql"
"time"
"github.com/jmoiron/sqlx"
)
// CharacterRepository centralizes all database access for the characters table.
type CharacterRepository struct {
db *sqlx.DB
}
// NewCharacterRepository creates a new CharacterRepository.
func NewCharacterRepository(db *sqlx.DB) *CharacterRepository {
return &CharacterRepository{db: db}
}
// LoadColumn reads a single []byte column by character ID.
func (r *CharacterRepository) LoadColumn(charID uint32, column string) ([]byte, error) {
var data []byte
err := r.db.QueryRow("SELECT "+column+" FROM characters WHERE id = $1", charID).Scan(&data)
return data, err
}
// SaveColumn writes a single []byte column by character ID.
func (r *CharacterRepository) SaveColumn(charID uint32, column string, data []byte) error {
_, err := r.db.Exec("UPDATE characters SET "+column+"=$1 WHERE id=$2", data, charID)
return err
}
// ReadInt reads a single integer column (0 for NULL) by character ID.
func (r *CharacterRepository) ReadInt(charID uint32, column string) (int, error) {
var value int
err := r.db.QueryRow("SELECT COALESCE("+column+", 0) FROM characters WHERE id=$1", charID).Scan(&value)
return value, err
}
// AdjustInt atomically adds delta to an integer column and returns the new value.
func (r *CharacterRepository) AdjustInt(charID uint32, column string, delta int) (int, error) {
var value int
err := r.db.QueryRow(
"UPDATE characters SET "+column+"=COALESCE("+column+", 0)+$1 WHERE id=$2 RETURNING "+column,
delta, charID,
).Scan(&value)
return value, err
}
// GetName returns the character name by ID.
func (r *CharacterRepository) GetName(charID uint32) (string, error) {
var name string
err := r.db.QueryRow("SELECT name FROM characters WHERE id=$1", charID).Scan(&name)
return name, err
}
// GetUserID returns the owning user_id for a character.
func (r *CharacterRepository) GetUserID(charID uint32) (uint32, error) {
var userID uint32
err := r.db.QueryRow("SELECT user_id FROM characters WHERE id=$1", charID).Scan(&userID)
return userID, err
}
// UpdateLastLogin sets the last_login timestamp.
func (r *CharacterRepository) UpdateLastLogin(charID uint32, timestamp int64) error {
_, err := r.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", timestamp, charID)
return err
}
// UpdateTimePlayed sets the time_played value.
func (r *CharacterRepository) UpdateTimePlayed(charID uint32, timePlayed int) error {
_, err := r.db.Exec("UPDATE characters SET time_played=$1 WHERE id=$2", timePlayed, charID)
return err
}
// GetCharIDsByUserID returns all character IDs belonging to a user.
func (r *CharacterRepository) GetCharIDsByUserID(userID uint32) ([]uint32, error) {
var ids []uint32
err := r.db.Select(&ids, "SELECT id FROM characters WHERE user_id=$1", userID)
return ids, err
}
// ReadTime reads a single time.Time column by character ID.
// Returns the provided default if the column is NULL.
func (r *CharacterRepository) ReadTime(charID uint32, column string, defaultVal time.Time) (time.Time, error) {
var t sql.NullTime
err := r.db.QueryRow("SELECT "+column+" FROM characters WHERE id=$1", charID).Scan(&t)
if err != nil {
return defaultVal, err
}
if !t.Valid {
return defaultVal, nil
}
return t.Time, nil
}
// SaveTime writes a single time.Time column by character ID.
func (r *CharacterRepository) SaveTime(charID uint32, column string, value time.Time) error {
_, err := r.db.Exec("UPDATE characters SET "+column+"=$1 WHERE id=$2", value, charID)
return err
}
// SaveInt writes a single integer column by character ID.
func (r *CharacterRepository) SaveInt(charID uint32, column string, value int) error {
_, err := r.db.Exec("UPDATE characters SET "+column+"=$1 WHERE id=$2", value, charID)
return err
}
// SaveBool writes a single boolean column by character ID.
func (r *CharacterRepository) SaveBool(charID uint32, column string, value bool) error {
_, err := r.db.Exec("UPDATE characters SET "+column+"=$1 WHERE id=$2", value, charID)
return err
}
// SaveString writes a single string column by character ID.
func (r *CharacterRepository) SaveString(charID uint32, column string, value string) error {
_, err := r.db.Exec("UPDATE characters SET "+column+"=$1 WHERE id=$2", value, charID)
return err
}
// ReadBool reads a single boolean column by character ID.
func (r *CharacterRepository) ReadBool(charID uint32, column string) (bool, error) {
var value bool
err := r.db.QueryRow("SELECT "+column+" FROM characters WHERE id=$1", charID).Scan(&value)
return value, err
}
// ReadString reads a single string column by character ID (empty string for NULL).
func (r *CharacterRepository) ReadString(charID uint32, column string) (string, error) {
var value sql.NullString
err := r.db.QueryRow("SELECT "+column+" FROM characters WHERE id=$1", charID).Scan(&value)
if err != nil {
return "", err
}
return value.String, nil
}
// LoadColumnWithDefault reads a []byte column, returning defaultVal if NULL.
func (r *CharacterRepository) LoadColumnWithDefault(charID uint32, column string, defaultVal []byte) ([]byte, error) {
var data []byte
err := r.db.QueryRow("SELECT "+column+" FROM characters WHERE id=$1", charID).Scan(&data)
if err != nil {
return defaultVal, err
}
if data == nil {
return defaultVal, nil
}
return data, nil
}
// SetDeleted marks a character as deleted.
func (r *CharacterRepository) SetDeleted(charID uint32) error {
_, err := r.db.Exec("UPDATE characters SET deleted=true WHERE id=$1", charID)
return err
}
// UpdateDailyCafe sets daily_time, bonus_quests, and daily_quests atomically.
func (r *CharacterRepository) UpdateDailyCafe(charID uint32, dailyTime time.Time, bonusQuests, dailyQuests uint32) error {
_, err := r.db.Exec("UPDATE characters SET daily_time=$1, bonus_quests=$2, daily_quests=$3 WHERE id=$4",
dailyTime, bonusQuests, dailyQuests, charID)
return err
}
// ResetDailyQuests zeroes bonus_quests and daily_quests.
func (r *CharacterRepository) ResetDailyQuests(charID uint32) error {
_, err := r.db.Exec("UPDATE characters SET bonus_quests=0, daily_quests=0 WHERE id=$1", charID)
return err
}
// ReadEtcPoints reads bonus_quests, daily_quests, and promo_points.
func (r *CharacterRepository) ReadEtcPoints(charID uint32) (bonusQuests, dailyQuests, promoPoints uint32, err error) {
err = r.db.QueryRow("SELECT bonus_quests, daily_quests, promo_points FROM characters WHERE id=$1", charID).
Scan(&bonusQuests, &dailyQuests, &promoPoints)
return
}
// ResetCafeTime zeroes cafe_time and sets cafe_reset.
func (r *CharacterRepository) ResetCafeTime(charID uint32, cafeReset time.Time) error {
_, err := r.db.Exec("UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2", cafeReset, charID)
return err
}
// UpdateGuildPostChecked sets guild_post_checked to now().
func (r *CharacterRepository) UpdateGuildPostChecked(charID uint32) error {
_, err := r.db.Exec("UPDATE characters SET guild_post_checked=now() WHERE id=$1", charID)
return err
}
// ReadGuildPostChecked reads guild_post_checked timestamp.
func (r *CharacterRepository) ReadGuildPostChecked(charID uint32) (time.Time, error) {
var t time.Time
err := r.db.QueryRow("SELECT guild_post_checked FROM characters WHERE id=$1", charID).Scan(&t)
return t, err
}
// SaveMercenary updates savemercenary and rasta_id atomically.
func (r *CharacterRepository) SaveMercenary(charID uint32, data []byte, rastaID uint32) error {
_, err := r.db.Exec("UPDATE characters SET savemercenary=$1, rasta_id=$2 WHERE id=$3", data, rastaID, charID)
return err
}
// UpdateGCPAndPact updates gcp and pact_id atomically.
func (r *CharacterRepository) UpdateGCPAndPact(charID uint32, gcp uint32, pactID uint32) error {
_, err := r.db.Exec("UPDATE characters SET gcp=$1, pact_id=$2 WHERE id=$3", gcp, pactID, charID)
return err
}
// FindByRastaID looks up name and id by rasta_id.
func (r *CharacterRepository) FindByRastaID(rastaID int) (charID uint32, name string, err error) {
err = r.db.QueryRow("SELECT name, id FROM characters WHERE rasta_id=$1", rastaID).Scan(&name, &charID)
return
}