@f_u_s_s
Любопытный кодер

Как правильно организовать балансы и лицевые счета в системе?

Веду разработку внутреннего продукта, что-то типа тикет-системы. Встал вопрос о реализации системы лицевых счетов клиентов. Толком какой-то полноценной информации не смог найти, потому из обрывков сообразил такую логику:

Есть таблицы (описаны самые базовые структуры)
company - собственно сама компания (id | name)
payments - платежи компании, по сути приходы (id | company_id | amount | timestamp)
write_offs - списания средств со сета компаний, по сути расходы (id | company_id | user_id | ticket_id | amount | timestamp)
company_balances - таблица балансов компаний (id | company_id | balance | action | action_id | checksum | timestamp)

Последняя таблица нужна, что бы не производить вычисления всей истории каждый раз, когда требуется проверка.

Актуальным считается последняя запись в таблице company_balances для конкретного company_id. Каждый раз, когда производится начисление или списание - система смотрит на текущий баланс и вычисляет соленый хеш исходя из данных в полях action (payment или write_ofs) и action_id (id в соответствующей таблице, указанной в action) (таблица company_balances) и сравнивает с имеющимся значением checksum в той же таблице. Если результат совпадает - значит можно производить операцию, если нет - значит баланс был изменен в обход алгоритма (например через PMA) и такой счет блокируется, уведомляется админ и производится уже выяснение причин, пока пускай будет в ручном режиме администратором/уполномоченным сотрудником.

При разрешении на операцию вычисляется новый баланс и его checksum, производится запись в соответствующую операции таблицу и в таблицу балансов. Все это в транзакциях и с блокировками соответствующих таблиц само-собой.

Собственно вопрос - насколько такой алгоритм верный, безопасен ли и как можно его упростить (не в ущерб безопасности) или доработать (для повышения безопасности, но не в ущерб производительности). Была еще мысль текущую checksum сделать зависимой от предыдущей, например использовать как часть соли при вычислении новой суммы, но пока не уверен насчет необходимости этого шага.

Сейчас реализована похожая система, но баланс вычисляется из приходов/расходов которые хранятся в одной таблице в одной колонке, что очень долго при огромном количестве данных. Ну и соответственно при доступе к БД легко правится любой баланс в любом периоде, что не допустимо.

Если есть статьи/книги по данной теме, не абстрактные, а с примерами алгоритмов и описанием причин тех или иных решений - буду крайне признателен. Спасибо!
  • Вопрос задан
  • 129 просмотров
Пригласить эксперта
Ответы на вопрос 2
@d-stream
Готовые решения - не подаю, но...
Ну если еще добавить отгрузки (реализации) и поступления ТМЦ - то собственно получается, что баланс - вычисляемое из сумм всех платежей/отгрузок. Ну иногда может потребоваться некий хелпер в виде готового значения чтобы каждый раз не суммировать.

p/s/ возможно вернее это будет звучать как "сальдо"
Ответ написан
Комментировать
@stratosmi
Подсчитывать итоги по балансу раз в неделю (раз в день, раз в месяц) и сохранять в таблицу баланса.
А текущие (за последний день, неделю, месяц, то есть те, что после того как занесена запись в таблицу баланса) приходы-расходы, все же, считать каждый раз.

Таким образом:
баланс текущий = баланс на предыдущий период + небольшое количество-приходов расходов что после "предыдущего периода" сделаны.

Удобно тем, что всегда можно выполнить полный пересчет баланса (выбрав как у сервера ненагруженное время).

Сброс баланса (в таблице итогов) в случае ручной правки можно сделать в тригере.
Таким образом, при ручной правке просто придется пересчитать итоги заново.

Там же в тригере можно запретить изменять приходы-расходы по уже расчитанным периодам.

Ваш вариант "блокировать до разборок" тоже имеет свои плюсы, но это означает ждать эти "до разборок", может и пару дней. А клиенту работать нужно.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы