В коде неверный идентификатор advertiser, SQL-инъекция, отсутствие транзакции и небезопасные денежные вычисления. Две записи и счётчик нельзя считать атомарной выплатой.
func (r *PayoutRepository) Payout(advertiserID, partnerID string, amount float32) error {
В коде неверный идентификатор advertiser, SQL-инъекция, отсутствие транзакции и небезопасные денежные вычисления. Две записи и счётчик нельзя считать атомарной выплатой.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
func (r *PayoutRepository) Payout(advertiserID, partnerID string, amount float32) error { advertiser, err := r.find(totalPayouts) // добавить транзакцию if err != nil { return err } partner, err := r.find(partnerID) if err != nil { return err }
advertiser.Balance -= amount
partner.Hold -= amount
partner.Balance += amount
if err := r.save(advertiser); err != nil {
return err
}
if err := r.save(partner); err != nil {
return err
}
r.totalPayouts++
return nil
}
func (r *PayoutRepository) find(id string) (Partner, error) { p := Partner{} err := r.db.QueryRow("SELECT * FROM partners WHERE id = " + id).Scan(&p) // добавить $1 if err != nil { if err == sql.ErrNoRows { return p, errors.New("partner not found") } return p, err } return p, nil }
Ответ
В коде неверный идентификатор advertiser, SQL-инъекция, отсутствие транзакции и небезопасные денежные вычисления. Две записи и счётчик нельзя считать атомарной выплатой.
r.find(totalPayouts) должно получать advertiserID. В find нужны явные столбцы, WHERE id = $1 и отдельные адреса полей для Scan, а не конкатенация и Scan(&p).
Выплату выполняют одной транзакцией через tx, блокируя обе записи в устойчивом порядке или используя корректные условные UPDATE. Проверяют сумму, баланс, hold и повтор команды; деньги хранят точным типом. Обрабатывают Commit и Rollback. r.totalPayouts требует синхронизации, но даже атомик не делает его долговечным или транзакционным счётчиком выплат.