laravel的订单管理api

这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

一、订单详情管理

1.1 创建订单控制器

运行命令php artisan make:controller Web/OrderController创建订单控制器: 在这里插入图片描述 写入订单管理相关的方法:

<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\BaseController;
use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class OrderController extends BaseController
{
	/**
     * 订单列表
     */
    public function index(Request $request) {
        // 状态查询
        $status = $request->query('status') ;

        // 根据标题
        $title = $request->query('title');

        $order = Order::where('user_id', auth('api')->id())
            ->when($status, function ($query) use ($status) {
                $query->where('status', $status);
            })
            ->when($status, function ($query) use ($title) {
                $query->whereHas('goods', function ($query) use($title) {
                    $query->where('title', 'like', "%{$title}%");
                }); // 向关联加入自定义约束
            })
            ->paginate(3);
        return $this->response->paginator($order, new OrderTransformer());
    }
	
    /**
     * 预览订单
     */
    public function preview()
    {
        // 地址数据
        // TODO 暂时模拟地址数据
        $address = [
            ['id' => 1, 'name' => '张三', 'address' => '厦门', 'phone' => '123123'],
            ['id' => 2, 'name' => '李四', 'address' => '厦门', 'phone' => '123123'],
            ['id' => 3, 'name' => '王武', 'address' => '厦门', 'phone' => '123123'],
        ];

        // 购物车数据
        $carts = Cart::where('user_id', auth('api')->id())
            ->where('is_checked', 1)
            ->with('goods:id,cove,title')
            ->get();

        // 返回数据
        return $this->response->array([
            'address' => $address,
            'carts' => $carts,
        ]);
    }

    /**
     * 提交订单
     */
    public function store(Request $request)
    {
        // 验证地址字段
        $request->validate([
            'address_id' => 'required' // TODO 地址要存在才行 exists:addresses,id
        ], [
            'address_id.required' => ['收获地址不能为空']
        ]);

        // 处理插入的数据
        $user_id = auth('api')->id();
        $order_no = date('YmdHis') . rand(100000, 999999);
        $amount = 0;
        $cartsQuery = Cart::where('user_id', $user_id)
            ->where('is_checked', 1);

        $carts = $cartsQuery->with('goods:id,price')->get();

        // 要插入的订单详情的数据
        $insertData = [];


        foreach ($carts as $key => $cart) {
        	// 如果有商品库存不足,提示用户重新选择
            if ($cart->goods->stock < $cart->num) {
                return $this->response->errorBadRequest($cart->goods->title.' 库存不足,请重新选择商品!');
            }
            $insertData[] = [
                'goods_id' => $cart->goods_id,
                'price' => $cart->goods->price,
                'num' => $cart->num
            ];
            $amount += $cart->goods->price * $cart->num;
        }

        try {
            DB::beginTransaction(); // 开启事务
            // 生成订单
            $order = Order::create([
                'user_id' => $user_id,
                'order_no' => $order_no,
                'address_id' => $request->input('address_id'),
                'amount' => $amount
            ]);

            // 生成订单的详情
            $order->orderDetails()->createMany($insertData);

            // 删除已经结算的购物车数据
            $cartsQuery->delete();
            
            // 减去商品对应的库存量
            foreach($carts as $cart) {
                Good::where('id', $cart->goods_id)->decrement('stock', $cart->num);
            }
            DB::commit(); // 没有异常才提交
            return $this->response->created();
        } catch (\Exception $e) {
            DB::rollBack(); // 出现异常 事务 数据回滚
            throw $e; // 抛出异常
        }
    }
    // 订单详情
    public function show(Order $order) {
        return $this->response->item($order, new OrderTransformer());
    }
	/**
     * 确认收货
     */
    public function confirm(Order $order) {
        if ($order->status != 3) {
            return $this->response->errorBadRequest('订单状态异常!');
        }
       try {
            DB::beginTransaction();
            $order->status = 4;
            $order->save();

            $orderDetails = $order->orderDetails;
            
            // 增加订单下所有商品的销量
            foreach($orderDetails as $orderDetail) {
                // 更新商品销量
                Good::where('id', $orderDetail->goods_id)->increment('sales', $orderDetail->num);
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
        return $this->response->noContent();
    }
}
复制代码

在这里插入图片描述

1、我们这边暂时还没做地址管理api,所以地址暂时用模拟 2、由于创建订单涉及到多表操作,万一一步操作错了,可能会产生未知的bug,所以我们这边使用事务来进行异常捕获并抛出。

由于往订单表中插入数据,所以我们还要往订单表中写入可批量赋值的字段: 在这里插入图片描述 由于也往订单详情表中插入了数据,所以也需要往订单详情表中插入可批量的字段: 在这里插入图片描述


接着在订单模型中写入远程一对多,这样我们就可以跳过中间订单详情表,直接通过订单模型获取到商品数据:

    /**
     * 订单远程一对多,关联商品
     */
    public function goods() {
        return $this->hasManyThrough(
            Good::class, // 最终关联的模型
            OrderDetails::class, // 中间模型
            'order_id', // 中间模型和本模型关联的外键
            'id', // 最终关联模型的外键
            'id', // 本模型和中间模型关联的健
            'goods_id' // 中间表和最终模型关联到一个键
        );
    }
复制代码

在这里插入图片描述

增加订单OrderTransformer.php的商品数据:

    /**
     * 商品数据
     */
    public function includeGoods(Order $order) {
        return $this->collection($order->goods, new GoodTransformer());
    }
复制代码

在这里插入图片描述

1、任务调度订单过时未付取消

由于下单了就会减少库存,为了防止有些人恶意下单,减库存,所以我们可以做个任务调度,做个订单多久失效。 输入命令:crontab -e,输入1回车: 在这里插入图片描述 在编辑器中输入* * * * * /home/vagrant/code/shopProjectApi/artisan schedule:run >> /dev/null 2>&1。你的项目的绝对路径。 然后controller + x输入y保存退出。 然后在 App\Console\Kernel 中输入:

// 定时检测订单状态,超过10分钟未支付的,作废
        // 真是项目一般会采用长链接,因为可以实时长链接,时间一到,直接作废,而任务调度的话不会那么精确的跟前端显示一样的时间
        // 任务调度的话 在一定的时间帮我们去执行任务 像备份数据...
        $schedule->call(function () {
            // info('hellp');
            $orders = Order::where('status', 1)
                    ->where('created_at', '<', date('Y-m-d H:i:s', time()-600))
                    ->with('orderDetails.goods') // 拿到关联的商品
                    ->get();

            // 循环订单,修改订单状态,还原商品库存
            try {
                DB::beginTransaction(); // 开启门面

                foreach($orders as $order) {
                    $order->status = 5;
                    $order->save();

                    // 还原商品库存
                    foreach($order->orderDetails as $details) {
                        $details->goods->increment('stock', $details->num);
                    }
                }

                Db::commit();
            } catch (\Exception $e) {
                DB::rollBack();
                Log::error($e);
            }

        })->everyMinute();
复制代码

在这里插入图片描述 这样在订单过期时,将会把订单状态设置成5,并且商品数量将回滚: 在这里插入图片描述


1.2 创建订单路由

        /**
         * 订单
         */
        // 订单预览页
        $api->get('order/preview', [OrderController::class, 'preview']);

        // 提交订单
        $api->post('order', [OrderController::class, 'store']);

		// 订单详情
        $api->get('order/{order}', [OrderController::class, 'show']);

		// 订单列表
        $api->get('orders', [OrderController::class, 'index']);
		
		// 确认收货
        $api->patch('orders/{order}/confirm', [OrderController::class, 'confirm']);
复制代码

在这里插入图片描述


1.3 测试效果

在这里插入图片描述

测试的时候我们发现报错了,原因是我们之前在建订单表时,将订单单号设置成了integer在这里插入图片描述 所以我们来修改下这个字段的类型。 首先运行命令composer require doctrine/dbal在这里插入图片描述 接着运行:php artisan make:migration modify_order_no_column_in_orders_table --table=orders创建修改该字段的迁移文件: 在这里插入图片描述 接着写入:$table->string('order_no')->comment('订单单号')->change(); 在这里插入图片描述 好的类型就修改好了。


接下来测试: 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

可以看到生成了订单,并且购物车中数据也没了。

在学习的php的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。

猜你喜欢

转载自juejin.im/post/7033566935153541134