H30秋基本情報技術者試験 問3 を RubyonRails で作ってみる

post on
post cover image

from Qiita: H30基本情報技術者試験問題3 DB問題

H30秋基本情報技術者試験問3のコンサートに則したサイトを実際に作っていた。

Refferrence

Transaction for Payment controller

コンサートチケットの支払い時の、ポイント使用・追加あたりの、Paymentコントローラ内に実装。

Transaction flow

  1. ユーザはポイントUser.pointを持っている。
  2. 購入時にUser.pointの一部/全部を支払額Sale.amountに充てることができる。
  3. 使用ポイントSale.used_pointが更新される
  4. 支払額から使用したUser.pointを引いたものが、決済額Payment.amountとなる。
  5. 決済額Payment.amountのうち、既定の割合が付与ポイントPayment.added_pointとなる。
  6. ユーザのポイント残高は、(支払前の)User.point - Sale.used_point + Payment.added_pointで更新される。

支払い完了の条件

  • User.point、Sale.used_point、Payment.added_pointは全て0以上(>=0)
    • モデル側のバリデーションvalidates :point, numericality: { greater_than_or_equal_to: 0 }利用
  • User.point >= Sale.used_point
    • Falseとなる操作は悪意しかないので、トランザクション外のif文で

Implement

User.point、Sale.used_point、Payment.added_pointは全て0以上(>=0)

# User model
validates :point, numericality: { greater_than_or_equal_to: 0 }
# Sale model
validates :used_point, numericality: { greater_than_or_equal_to: 0 }
# Payment model
validates :added_point, numericality: { greater_than_or_equal_to: 0 }
# users_controller.rb
def payment
    @user = # ...
    @sale = # ...
    @concert = # ...
    @payment = Payment.new(sale_id: @sale.id, date: Date.current)
    respond_to do |format|
      if current_user.point < params_used_point 
        # 所持ポイントを超過している旨の警告文 (以下、ポイントをPと略す)
      else # when pay with appropriate points
        begin
          ActiveRecord::Base.transaction do
            if @sale.amount <= params_used_point # when using points is over sale price
              @sale.update!(used_point: @sale.amount) # succeeded in paying
              @payment.update(amount: 0, added_point: 0) 
            else
              @sale.update!(used_point: params_used_point)
              @payment.update!(amount: pay_amount, added_point: 追加P計算関数)
            end
            @user.update!(point: ユーザP更新関数) # completed to pay
          end
        rescue StandardError => e # if transaction failed
          logger.error e
          logger.error e.backtrace.join("\n")
          @sale = # ...
          @concert = # ...
          format.html { render :confirm, notice: 'エラー' }
        end
      end
    end
end

まあ、駄目な部分もあると思う。が、まあ、