You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Rails中通过JSON POST实现多表更新及库存校验扣减

Hey there! Let's tackle your two requirements step by step—stock validation and inventory deduction when creating an order via that POST request. Here's how to implement this properly in your Rails app, following best practices for MVC and data integrity:

Solution: Stock Validation & Inventory Deduction for Order Creation

We'll keep most logic in the models (per Rails conventions) to keep controllers lean, and use database transactions to ensure no partial inventory updates if order creation fails.

Your line item model is named OrderIten—that's likely a typo for OrderItem. If you can rename it (and run the necessary migration/rename commands), it'll make your code more readable. We'll stick with OrderIten here to match your existing codebase.

2. Add Stock Availability Validation to the Order Model

First, we'll add a custom validation to check if the subsidiary has enough stock for all items in the order. This handles cases where the same product is added multiple times (like your example with two entries for product 1) by summing total quantity needed per product.

Update your Order model:

class Order < ApplicationRecord
  belongs_to :user
  belongs_to :subsidiary
  has_many :order_itens
  accepts_nested_attributes_for :order_itens

  # Custom validation to check stock levels
  validate :ensure_sufficient_stock

  private

  def ensure_sufficient_stock
    # Group order items by product to calculate total quantity needed
    product_needs = order_itens.group(:product_id).sum(:quantity)

    product_needs.each do |product_id, total_needed|
      # Find the inventory record for this product and order's subsidiary
      inventory = Inventory.find_by(product_id: product_id, subsidiary_id: subsidiary_id)

      if inventory.nil?
        errors.add(:base, "Product ##{product_id} is not available at this subsidiary")
      elsif inventory.quantity < total_needed
        errors.add(:base, "Insufficient stock for product ##{product_id}: requested #{total_needed}, available #{inventory.quantity}")
      end
    end
  end
end

3. Add Inventory Deduction with Atomic Transactions

Next, we'll use a before_create callback wrapped in a transaction to deduct stock only if the order is valid and about to be saved. This ensures that if any part of the order creation fails, inventory levels stay unchanged.

Update the Order model to include deduction logic:

class Order < ApplicationRecord
  belongs_to :user
  belongs_to :subsidiary
  has_many :order_itens
  accepts_nested_attributes_for :order_itens

  validate :ensure_sufficient_stock
  before_create :deduct_inventory_stock

  private

  def ensure_sufficient_stock
    product_needs = order_itens.group(:product_id).sum(:quantity)

    product_needs.each do |product_id, total_needed|
      inventory = Inventory.find_by(product_id: product_id, subsidiary_id: subsidiary_id)

      if inventory.nil?
        errors.add(:base, "Product ##{product_id} is not available at this subsidiary")
      elsif inventory.quantity < total_needed
        errors.add(:base, "Insufficient stock for product ##{product_id}: requested #{total_needed}, available #{inventory.quantity}")
      end
    end
  end

  def deduct_inventory_stock
    # Use a transaction to make all deductions atomic
    Order.transaction do
      product_needs = order_itens.group(:product_id).sum(:quantity)

      product_needs.each do |product_id, total_needed|
        inventory = Inventory.lock.find_by(product_id: product_id, subsidiary_id: subsidiary_id)
        # Use update! to raise an error if the update fails (rolls back transaction)
        inventory.update!(quantity: inventory.quantity - total_needed)
      end
    end
  end

We added lock to the inventory query to prevent race conditions in production—this acquires a row-level lock so no other transaction can modify the inventory record until this one completes.

4. Controller Adjustment (Minor)

Your existing controller code is mostly fine—we just need to make sure it handles any exceptions gracefully (though the validation should catch most issues before we reach the deduction step). Here's the cleaned-up version:

class OrdersController < ApplicationController
  # POST /orders
  # POST /orders.json
  def create
    @order = Order.new(order_params)

    if @order.save
      render json: @order, include:[:order_itens], status: :created, location: @order
    else
      render json: @order.errors, status: :unprocessable_entity
    end
  end

  private

  def order_params
    params.require(:order).permit(
      :user_id, :subsidiary_id, :amount, :start_time, :arrive_time,
      :delivered_time, :cancel_time,
      order_itens_attributes: [:quantity,:product_id]
    )
  end
end

5. How It Works with Your Example Request

Let's test with your sample JSON request:

{ 
  "order": { 
    "subsidiary_id":1, 
    "user_id":1, 
    "amount":220, 
    "order_itens_attributes": [ 
      { "quantity":22, "product_id": 1 }, 
      { "quantity":22, "product_id": 1 } 
    ] 
  } 
}
  1. Validation: The order sums the two line items to 44 units needed for product 1 in subsidiary 1. It checks the Inventory record—if stock is >=44, validation passes; if not, errors are returned.
  2. Deduction: If validation passes, the transaction deducts 44 units from the inventory. The order saves successfully, and you get the same success response you shared earlier.

内容的提问来源于stack exchange,提问作者Javier Joasimar Palos Flores

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:07:46