Files
back-hokm/internal/economy/wallet.go
T
2026-06-15 16:53:25 +03:30

75 lines
2.0 KiB
Go

package economy
import (
"context"
"database/sql"
"errors"
"fmt"
)
// ارزها.
const (
CurrencyCoin = "coin"
CurrencyTicket = "ticket"
)
var (
ErrInsufficient = errors.New("insufficient balance")
ErrBadCurrency = errors.New("invalid currency")
)
// column نام ستون موجودی را برای یک ارز برمی‌گرداند.
func column(currency string) (string, error) {
switch currency {
case CurrencyCoin:
return "coins", nil
case CurrencyTicket:
return "tickets", nil
}
return "", ErrBadCurrency
}
// adjustTx موجودی را داخل یک تراکنش تغییر داده و در دفتر ثبت می‌کند.
// برای delta منفی، اگر موجودی کافی نباشد ErrInsufficient برمی‌گرداند (بدون تغییر).
func adjustTx(ctx context.Context, tx *sql.Tx, userID int64, currency string, delta int64, reason, ref string) error {
col, err := column(currency)
if err != nil {
return err
}
var res sql.Result
if delta < 0 {
// فقط در صورت کفایت موجودی کم کن (اتمیک)
res, err = tx.ExecContext(ctx,
fmt.Sprintf("UPDATE users SET %s = %s + ? WHERE id = ? AND %s >= ?", col, col, col),
delta, userID, -delta)
} else {
res, err = tx.ExecContext(ctx,
fmt.Sprintf("UPDATE users SET %s = %s + ? WHERE id = ?", col, col),
delta, userID)
}
if err != nil {
return err
}
n, _ := res.RowsAffected()
if n == 0 {
return ErrInsufficient
}
_, err = tx.ExecContext(ctx,
"INSERT INTO wallet_tx (user_id, amount, reason, ref, currency) VALUES (?, ?, ?, ?, ?)",
userID, delta, reason, ref, currency)
return err
}
// Adjust موجودی را در یک تراکنش مستقل تغییر می‌دهد.
func (s *Service) Adjust(ctx context.Context, userID int64, currency string, delta int64, reason, ref string) error {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
if err := adjustTx(ctx, tx, userID, currency, delta, reason, ref); err != nil {
return err
}
return tx.Commit()
}