75 lines
2.0 KiB
Go
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()
|
|
}
|