laravel的订单管理api 一、订单详情管理

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

一、订单详情管理

1.1 创建订单控制器

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
php复制代码<?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,所以我们这边使用事务来进行异常捕获并抛出。

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
php复制代码    /**
* 订单远程一对多,关联商品
*/
public function goods() {
return $this->hasManyThrough(
Good::class, // 最终关联的模型
OrderDetails::class, // 中间模型
'order_id', // 中间模型和本模型关联的外键
'id', // 最终关联模型的外键
'id', // 本模型和中间模型关联的健
'goods_id' // 中间表和最终模型关联到一个键
);
}

在这里插入图片描述

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

1
2
3
4
5
6
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 中输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
php复制代码// 定时检测订单状态,超过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 创建订单路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
php复制代码        /**
* 订单
*/
// 订单预览页
$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的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%