Обеспечение целостности баланса пользователя в приложении Rails - PullRequest
2 голосов
/ 23 февраля 2011

«Баланс» представляет собой разницу между суммой кредитов и суммой дебетов. Следующая упрощенная схема представляет пример:

Схема http://dl.dropbox.com/u/10177092/Personal/stackoverflow_question.png

Обратите внимание на столбец «Баланс». Я определил три подхода к определению баланса:

(1) При обновлении таблицы кредитов или дебетов (и обновлении баланса) оберните код в Блок транзакций . Например:

ActiveRecord::Base.transaction do
  current_user.credits.credit(100)
  current_user.balance.increment(100)
end

(2) Не включайте столбец «баланс» - вместо этого вычисляйте баланс каждый раз, когда он запрашивается:

credit = current_user.credits.sum('amount')
debit = current_user.debits.sum('amount')
balance = credits - debits

(3) Создать представление базы данных .

Буду признателен за совет относительно:

  • Преимущества и недостатки каждого подхода.
  • Альтернативные подходы.

Мне кажется, что вычисление баланса (например, 2. и 3.) наилучшим образом обеспечивает целостность значения. Тем не менее, я обеспокоен (2) может стать неэффективным, поскольку пользователи участвуют в дополнительных транзакциях. Представления базы данных кажутся теоретически обоснованным вариантом, но я не верю, что rails_sql_views поддерживает Rails 3, и я заметил несколько потоков, которые подразумевают, что представления базы данных нежелательны / часто связаны с устаревшими базами данных.

1 Ответ

3 голосов
/ 23 февраля 2011

Создание модели Движение .

user_id, quantity, balance, updated_at, created_at

Определение методов экземпляра add_credit, add_debit и balance в Пользователь модель:

 def add_credit(quantity)   
    self.movements.create :quantity => quantity 
 end

 def add_debit(quantity)
    self.movements.create :quantity => -quantity
end

def balance
  self.movements.last.balance
end

Используйте обратный вызов after_save в модели Movement :

before_save :update_balance

def update_balance
    if balance
       balance = self.user.movements.last.balance + self.quantity 
    else
       balance = self.quantity # First movement
    end
end

Таким образом вы гарантируете, что при добавлении кредита или дебета баланс обновляется, поскольку он содержится в том же объекте движения кредита / дебета.

...