@@ -0,0 +1,86 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"git.sundynix.cn/Blizzard/Veyra/internal/apperror"
|
||||
"git.sundynix.cn/Blizzard/Veyra/internal/model"
|
||||
)
|
||||
|
||||
type AccountRepository struct{ pool *pgxpool.Pool }
|
||||
|
||||
func NewAccountRepository(pool *pgxpool.Pool) *AccountRepository {
|
||||
return &AccountRepository{pool: pool}
|
||||
}
|
||||
|
||||
func (r *AccountRepository) Create(ctx context.Context, a model.Account, entry *model.CashEntry) (model.AccountSummary, error) {
|
||||
tx, err := r.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
return model.AccountSummary{}, apperror.ErrUnavailable
|
||||
}
|
||||
defer tx.Rollback(context.Background())
|
||||
// Lock the owner so account creation cannot race with disabling the user.
|
||||
var status string
|
||||
if err := tx.QueryRow(ctx, `SELECT status FROM veyra_user WHERE id=$1 AND role='USER' FOR UPDATE`, a.UserID).Scan(&status); err != nil {
|
||||
return model.AccountSummary{}, mapError(err)
|
||||
}
|
||||
if status != "ACTIVE" {
|
||||
return model.AccountSummary{}, apperror.ErrForbidden
|
||||
}
|
||||
err = tx.QueryRow(ctx, `INSERT INTO veyra_account(id,user_id,mode,name,currency,ledger_status) VALUES($1,$2,$3,$4,$5,$6) RETURNING created_at`, a.ID, a.UserID, a.Mode, a.Name, a.Currency, a.LedgerStatus).Scan(&a.CreatedAt)
|
||||
if err != nil {
|
||||
return model.AccountSummary{}, mapError(err)
|
||||
}
|
||||
var balance *string
|
||||
if entry != nil {
|
||||
if _, err := tx.Exec(ctx, `INSERT INTO veyra_cash_entry(id,account_id,entry_type,amount_minor) VALUES($1,$2,$3,$4)`, entry.ID, entry.AccountID, entry.EntryType, entry.AmountMinor); err != nil {
|
||||
return model.AccountSummary{}, mapError(err)
|
||||
}
|
||||
value := model.FormatMoney(entry.AmountMinor)
|
||||
balance = &value
|
||||
}
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return model.AccountSummary{}, apperror.ErrUnavailable
|
||||
}
|
||||
return model.AccountSummary{Account: a, CashBalance: balance}, nil
|
||||
}
|
||||
|
||||
const summarySelect = `SELECT a.id,a.user_id,a.mode,a.name,a.currency,a.ledger_status,a.created_at,CASE WHEN a.ledger_status='COMPLETE' THEN (SELECT COALESCE(sum(e.amount_minor),0)::bigint FROM veyra_cash_entry e WHERE e.account_id=a.id) ELSE NULL END FROM veyra_account a`
|
||||
|
||||
func scanSummary(row pgx.Row) (model.AccountSummary, error) {
|
||||
var s model.AccountSummary
|
||||
var minor *int64
|
||||
err := row.Scan(&s.ID, &s.UserID, &s.Mode, &s.Name, &s.Currency, &s.LedgerStatus, &s.CreatedAt, &minor)
|
||||
if err != nil {
|
||||
return s, mapError(err)
|
||||
}
|
||||
if minor != nil {
|
||||
value := model.FormatMoney(*minor)
|
||||
s.CashBalance = &value
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (r *AccountRepository) Get(ctx context.Context, userID, accountID string) (model.AccountSummary, error) {
|
||||
return scanSummary(r.pool.QueryRow(ctx, summarySelect+` WHERE a.user_id=$1 AND a.id=$2`, userID, accountID))
|
||||
}
|
||||
|
||||
func (r *AccountRepository) List(ctx context.Context, userID string) ([]model.AccountSummary, error) {
|
||||
rows, err := r.pool.Query(ctx, summarySelect+` WHERE a.user_id=$1 ORDER BY a.created_at,a.id`, userID)
|
||||
if err != nil {
|
||||
return nil, apperror.ErrUnavailable
|
||||
}
|
||||
defer rows.Close()
|
||||
list := make([]model.AccountSummary, 0)
|
||||
for rows.Next() {
|
||||
s, err := scanSummary(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list = append(list, s)
|
||||
}
|
||||
return list, mapError(rows.Err())
|
||||
}
|
||||
Reference in New Issue
Block a user