• 感谢!

  • @vincent 好的,非常感谢您的建议!

  • 这里的order_item应该不会让用户直接操作,所有没有验证的问题。失败是正常,如果保存失败,能保证回滚和处理异常就好。创建之前都要执行validate的。

  • 嗯,controller加ActiveRecord::Base.transaction do,觉得有些不对味。

  • 感谢。

    如果按照您这样 create order 和 create order_items解决了, 还有最后一步逻辑,下单后,清空购物车。

    Cart.where(user_id: current_user.id).destroy_all
    

    这个应该包含在事务中?这个逻辑该放在哪里呢?

  • @vincent , @mengqing 改造了一下,如果使用ActiveRecord::Base.transaction的话

    将create逻辑放到了controller中核心代码如下:

    def create
        ActiveRecord::Base.transaction do
          @order = Order.new(user_id: current_user.id, orderID: @orderID , message:params[:message], fixed_address: Address.find(address_id).full_address, total_price: @total)
          @order_saved = @order.save!
          current_user.carts.each do |cart|
            OrderItem.new(user_id: current_user.id, orderID: @orderID, product_id: cart.product_id, price: cart.product.price, amount: cart.amount).save!
            # raise "crash for test"
          end
          #raise "crash for test"
          Cart.where(user_id: current_user.id).destroy_all
        end
    end
    

    将delete逻辑放到了controller中核心代码如下:

    def destroy
        ActiveRecord::Base.transaction do
          OrderItem.where(orderID: @order.orderID).destroy_all
          @order.destroy!
        end
    end
    
  • @mengqing 这个明白了,多谢!

    Any exception that is not ActiveRecord::Rollback or ActiveRecord::RecordInvalid will be re-raised by Rails after the callback chain is halted. Raising an exception other than ActiveRecord::Rollback or ActiveRecord::RecordInvalid may break code that does not expect methods like save and update_attributes (which normally try to return true or false) to raise an exception.

  • 对于第4点,测试一下回滚。

    在each循环里面,抛出异常,测试是否能回滚。

    def create_order_items
          current_user = self.user
          current_user.carts.each do |cart|
            OrderItem.new(user_id: current_user.id, orderID: self.orderID, product_id: cart.product_id, price: cart.product.price, amount: cart.amount).save
            # 抛出异常
            raise "crash for test"
          end
          # raise "crash for test"
          Cart.where(user_id: current_user.id).destroy_all
        end
    

    日志如下,可以看到,已经ROLLBACK

    Started POST "/orders" for ::1 at 2017-02-28 21:28:36 +0800
    Processing by OrdersController#create as HTML
      Parameters: {"address"=>"8", "pay_method"=>"alipay"}
      User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 4], ["LIMIT", 1]]
      Cart Load (0.6ms)  SELECT "carts".* FROM "carts" WHERE "carts"."user_id" = $1  [["user_id", 4]]
      Product Load (0.2ms)  SELECT  "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
      Address Load (0.2ms)  SELECT  "addresses".* FROM "addresses" WHERE "addresses"."id" = $1 LIMIT $2  [["id", 8], ["LIMIT", 1]]
       (0.1ms)  BEGIN
      SQL (0.4ms)  INSERT INTO "orders" ("user_id", "orderID", "created_at", "updated_at", "fixed_address", "total_price") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"  [["user_id", 4], ["orderID", "1488288516220"], ["created_at", 2017-02-28 13:28:36 UTC], ["updated_at", 2017-02-28 13:28:36 UTC], ["fixed_address", "李明 17030006000 北京 北京 海淀区 青松国际 36203"], ["total_price", #<BigDecimal:7f98c1e6e680,'0.25E1',18(27)>]]
      User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
      Cart Load (0.3ms)  SELECT "carts".* FROM "carts" WHERE "carts"."user_id" = $1  [["user_id", 4]]
      Product Load (0.2ms)  SELECT  "products".* FROM "products" WHERE "products"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
      SQL (0.5ms)  INSERT INTO "order_items" ("orderID", "product_id", "price", "amount", "created_at", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["orderID", "1488288516220"], ["product_id", 4], ["price", #<BigDecimal:7f98c2152d60,'0.25E1',18(27)>], ["amount", #<BigDecimal:7f98c2152950,'0.1E1',9(18)>], ["created_at", 2017-02-28 13:28:36 UTC], ["updated_at", 2017-02-28 13:28:36 UTC], ["user_id", 4]]
       (0.2ms)  ROLLBACK
    Completed 500 Internal Server Error in 43ms (ActiveRecord: 3.5ms)
    

    结果是能够回滚,通过界面测试也没问题,订单和购物车都还是原样不变,只是我这里还没有加异常判断。

  • 非常感谢@mengqing 的指点。 第1点,加index是个不错的主意! 第2点. 这里并不是has_many的关系,orderID是生成后插入的,2张表都有的,相同的。 其他几点我再考虑下,怎么调整。

  • 嗯,不打扫灰尘就会多。。。😄

运维工程师,Rails工程师。