OrderOperCreate.php
20.3 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
<?php
namespace addons\groupon\library\traits\model\order;
use addons\groupon\exception\Exception;
use addons\groupon\model\ScoreGoodsSkuPrice;
use addons\groupon\model\Goods;
use addons\groupon\model\User;
use addons\groupon\model\UserAddress;
use addons\groupon\model\UserCoupons;
use addons\groupon\model\Dispatch;
use think\Cache;
use think\Db;
trait OrderOperCreate
{
/**
* 获取请求参数,初始化,并设置默认值
*
* @param array $params
* @return array
*/
public static function preParams($params) {
extract($params);
$store_id = $store_id ?? 0;
return compact(
"goods_list",
"store_id",
"coupons_id",
"from"
);
}
/**
* 下单前检测,商品状态,活动状态,必要的选择项等
*
* @param array $params,请求参数
* @return array
*/
public static function preCheck($params, $calc_type) {
$user = User::info();
// 获取请求参数
extract(self::preParams($params));
$activity_type = '';
$new_goods_list = [];
$activity_discounts = [];
foreach ($goods_list as $key => $buyinfo) {
// 最少购买一件
$buyinfo['goods_num'] = intval($buyinfo['goods_num']) < 1 ? 1 : intval($buyinfo['goods_num']);
$sku_price_id = $buyinfo['sku_price_id'];
$detail = Goods::getGoodsDetail($buyinfo['goods_id']);
// 有活动,并且还未被拼接过
if (isset($detail['activity_type']) && $detail['activity_type'] && strpos($activity_type, $detail['activity_type']) === false) {
$activity_type .= $detail['activity_type'] . ','; // 记录所有活动信息
}
// 商品参与的有折扣活动
if (isset($detail['activity_discounts']) && $detail['activity_discounts']) {
$activity_discounts = array_merge($activity_discounts, $detail['activity_discounts']);
}
$sku_prices = $detail['sku_price'];
foreach ($sku_prices as $key => $sku_price) {
if ($sku_price['id'] == $sku_price_id) {
$detail->current_sku_price = $sku_price;
break;
}
}
if (!$detail || $detail->status === 'down') {
throw new Exception('商品不存在或已下架');
}
if (!isset($detail->current_sku_price) || !$detail->current_sku_price) {
throw new Exception('商品规格不存在');
}
$buyinfo['dispatch_type'] = 'selfetch'; // 配送方式全是自提
// 组装 商品详情
$buyinfo['detail'] = $detail;
$new_goods_list[] = $buyinfo;
if ($detail->current_sku_price['stock'] < $buyinfo['goods_num']) {
// 不够自己买
throw new Exception('商品库存不足');
}
}
if (!count($new_goods_list)) {
throw new Exception('请选择要购买的商品');
}
return [
$new_goods_list,
$activity_discounts,
$activity_type
];
}
/**
* 计算订单各种费用
*
* @param array $new_goods_list
* @return array
*/
public static function preCalcAmount($params, $new_goods_list) {
// 获取请求参数
extract(self::preParams($params));
$goods_original_amount = 0; // 商品原始总价
$goods_amount = 0; // 商品总价
$total_amount = 0; // 订单总金额
$total_fee = 0; // 支付金额
$discount_fee = 0; // 折扣总金额
$coupon_fee = 0; // 优惠券金额
// 计算商品金额
foreach ($new_goods_list as $key => $buyinfo) {
$detail = $buyinfo['detail'];
// 当前商品原始总价
$current_goods_original_amount = bcmul($detail->original_price, $buyinfo['goods_num'], 2);
$goods_original_amount = bcadd($goods_original_amount, $current_goods_original_amount, 2);
// 当前商品现在总价
$current_goods_amount = bcmul($detail->current_sku_price->price, $buyinfo['goods_num'], 2);
$goods_amount = bcadd($goods_amount, $current_goods_amount, 2);
// 用户选择的门店
$current_store_id = $store_id ?? 0;
// 将计算好的属性记录下来,插入订单 item 表使用
$new_goods_list[$key]['goods_original_amount'] = $current_goods_original_amount;
$new_goods_list[$key]['goods_amount'] = $current_goods_amount;
$new_goods_list[$key]['store_id'] = $current_store_id;
$new_goods_list[$key]['activity_type'] = '';
$new_goods_list[$key]['discount_fee'] = 0; // 初始化每个商品分配到的优惠
}
return [
$new_goods_list, // 新的商品列表
$goods_original_amount, // 商品原始总价
$goods_amount, // 商品总价
];
}
/**
* 计算商品的优惠,优惠促销 和 优惠券
*
* @param array $new_goods_list
* @param array $activity_discounts
* @param string $activity_type
* @return array
*/
public static function preCalcDiscount(
$params,
$new_goods_list,
$activity_type,
$activity_discounts,
$goods_amount
) {
// 获取请求参数
extract(self::preParams($params));
$activity_discount_infos = []; // 参与的所有促销活动信息
$activity_discount_money = 0; // 促销活动金额
$activity_gift_money = 0; // 赠送总额
$activity_gift_coupon_ids = ''; // 赠送的优惠券 ids
$activity_type = ''; // 参与的活动
$discounts = [];
// 过滤重复活动
foreach ($activity_discounts as $activity_discount) {
if (!isset($discounts[$activity_discount['id']])){
$discounts[$activity_discount['id']] = $activity_discount;
}
}
// 将购买的商品,按照活动分类
foreach ($new_goods_list as $new_goods) {
$detail = $new_goods['detail'];
unset($new_goods['detail']);
if (isset($detail['activity_discounts']) && $detail['activity_discounts']) {
foreach ($detail['activity_discounts'] as $ad) {
$discounts[$ad['id']]['goods'][] = $new_goods;
}
}
}
// 计算各个活动是否满足
foreach($discounts as $key => $discount) {
if (!isset($discount['goods'])) {
// 活动没有商品,直接 next
continue;
}
$discount_total_money = 0; // 该活动中商品的总价
$discount_total_num = 0; // 该活动商品总件数
$goodsIds = []; // 该活动中所有的商品 id
// 活动中的商品总金额,总件数,所有商品 id
foreach ($discount['goods'] as $goods) {
$discount_total_money = bcadd($discount_total_money, $goods['goods_amount'], 2);
$discount_total_num = bcadd($discount_total_num, $goods['goods_num'], 2);
$goodsIds[] = $goods['goods_id'];
}
$rules = $discount['rules'];
// 是按金额,还是按件数比较
$compareif = $rules['type'] == 'num' ? 'discount_total_num' : 'discount_total_money';
if (in_array($discount['type'], ['full_reduce', 'full_discount', 'full_gift'])) {
// 将规则按照从大到校排列,优先比较是否满足最大规则
$rules_discounts = isset($rules['discounts']) && $rules['discounts'] ? array_reverse($rules['discounts']) : []; // 数组反转
// 满减, 满折多个规则从大到小匹配最优惠
foreach ($rules_discounts as $d) {
if (${$compareif} < $d['full']) {
// 不满足条件,接着循环下个规则
continue;
}
$current_activity_discount_money = 0; // 优惠金额, full_gift 为 0
$current_activity_gift_money = 0; // 赠送金额, full_gift 时候存在
// 满足优惠
if ($discount['type'] == 'full_reduce') {
// 满减
$current_activity_discount_money = (isset($d['discount']) && $d['discount']) ? $d['discount'] : 0;
} else if ($discount['type'] == 'full_discount') {
// 满折
$dis = bcdiv($d['discount'], 10, 3); // 保留三位小数,转化折扣
$dis = $dis > 1 ? 1 : ($dis < 0 ? 0 : $dis); // 定义边界 0 - 1
$activity_dis = 1 - $dis;
$current_activity_discount_money = round(bcmul($discount_total_money, $activity_dis, 3), 2); // 计算折扣金额,四舍五入
} else if ($discount['type'] == 'full_gift') {
// 满赠
$current_activity_gift_money = (isset($d['total']) && $d['total']) ? $d['total'] : 0;
// 拼接需要赠送的优惠券 ids
$activity_gift_coupon_ids .= $d['coupons_ids'] . ',';
}
// 记录该活动的一些统计信息
$activity_discount_infos[] = [
'activity_id' => $discount['id'], // 活动id
'activity_title' => $discount['title'], // 活动标题
'activity_type' => $discount['type'], // 活动类型
'activity_discount_money' => $current_activity_discount_money, // 优惠金额, full_gift 为 0
'activity_gift_money' => $current_activity_gift_money, // 赠送金额, full_gift 时候存在
'rule_type' => $rules['type'], // 满多少元|还是满多少件
'event' => $rules['event'] ?? '', // 满赠的时候才会有,赠送时机
'discount_rule' => $d, // 满足的那条规则
'goods_ids' => join(',', $goodsIds) // 这个活动包含的这次购买的商品
];
// 累加促销活动总计优惠金额
$activity_discount_money = bcadd($activity_discount_money, $current_activity_discount_money, 2);
// 累加促销活动总计赠送金额
$activity_gift_money = bcadd($activity_gift_money, $current_activity_gift_money, 2);
// 拼接参与的活动类型,只拼接没拼接过的
if (strpos($activity_type, $discount['type']) === false) {
$activity_type .= $discount['type'] . ',';
}
break;
}
}
}
// 多活动拼接去掉多余的 ,
$activity_type = rtrim($activity_type, ',');
$activity_gift_coupon_ids = rtrim($activity_gift_coupon_ids, ',');
// 计算优惠券费用
$user_coupons = null;
$coupon_money = 0;
if ($coupons_id) {
// 查询传来的优惠券 id 是否可用
$coupons = self::coupons($params, $goods_amount);
$current_coupons = null; // 当前所选优惠券
foreach ($coupons as $key => $coupon) {
if ($coupon['user_coupons_id'] == $coupons_id) {
$current_coupons = $coupon;
break;
}
}
if ($current_coupons) {
$coupon_money = $current_coupons->amount; // 金额在 coupons 表存着
$user_coupons = UserCoupons::where('id', $coupons_id)->find(); // 用户优惠券
} else {
throw new Exception('优惠券不可用');
}
}
if ($activity_discount_infos) {
// 将每个商品对应的 activity_type 放入 new_goods_list
foreach($activity_discount_infos as $info) {
$goodsIds = explode(',', $info['goods_ids']);
foreach ($goodsIds as $goods_id) {
// 寻找商品id 等于 $goods_id 的所有商品,存在同一个商品,购买多个规格的情况
foreach ($new_goods_list as $g_k => $goods) {
if ($goods['goods_id'] == $goods_id) {
if (strpos($new_goods_list[$g_k]['activity_type'], $info['activity_type']) === false) {
$new_goods_list[$g_k]['activity_type'] .= $info['activity_type'] . ',';
}
}
}
}
}
// 去除多余的 ,
foreach($new_goods_list as $key => $goods) {
$new_goods_list[$key]['activity_type'] = rtrim($new_goods_list[$key]['activity_type'], ',');
}
}
return [
$new_goods_list,
$activity_discount_infos,
$activity_discount_money,
$activity_gift_money,
$activity_gift_coupon_ids,
$activity_type,
$user_coupons,
$coupon_money
];
}
/**
* 计算订单费用
*
* @param array $new_goods_list
* @param float $goods_amount
* @param float $origin_dispatch_amount 原始运费
* @param float $dispatch_amount
* @param int $score_amount
* @param float $activity_discount_money
* @param float $coupon_money
* @return array
*/
public static function preCalcOrder(
$new_goods_list,
$goods_amount,
$activity_discount_infos,
$activity_discount_money,
$coupon_money
) {
$total_amount = $goods_amount;
$coupon_fee = $coupon_money;
$discount_fee = bcadd($coupon_money, $activity_discount_money, 2);
$total_fee = bcsub($total_amount, $discount_fee, 2);
$total_fee = $total_fee < 0 ? 0 : $total_fee;
$new_goods_list = self::calcDiscountFee(
$new_goods_list,
$goods_amount,
$discount_fee,
$activity_discount_infos,
$activity_discount_money
);
return [
$new_goods_list,
$total_amount,
$discount_fee,
$total_fee,
$coupon_fee,
];
}
/**
* 处理返回结果
*
* @param float $goods_original_amount
* @param float $goods_amount
* @param float $total_amount
* @param float $total_fee
* @param float $discount_fee
* @param float $coupon_fee
* @param float $activity_discount_money
* @param string $activity_type
* @param array $new_goods_list
* @param array $activity_discount_infos
* @param array $user_coupons
* @param string $calc_type
* @return array
*/
public static function preReturnParams(
$goods_original_amount,
$goods_amount,
$total_amount,
$total_fee,
$discount_fee,
$coupon_fee,
$activity_discount_money,
$activity_gift_money,
$activity_gift_coupon_ids,
$activity_type,
$new_goods_list,
$activity_discount_infos,
$user_coupons,
$calc_type // 计算方式
) {
// 需要处理小数点的数据
$result = compact(
"goods_original_amount",
"goods_amount",
"total_amount",
"total_fee",
"discount_fee",
"coupon_fee",
"activity_discount_money",
"activity_gift_money"
);
// 处理小数点保留两位小数
foreach ($result as $key => $amount) {
$result[$key] = number_format($amount, 2, '.', '');
}
// 合并不需要处理小数点的
$result = array_merge($result, compact(
"activity_type",
"new_goods_list",
"activity_discount_infos",
"activity_gift_coupon_ids"
));
// 如果是下单,合并 优惠券, 收货地址
if ($calc_type == 'create') {
$result = array_merge($result, compact(
"user_coupons"
));
}
return $result;
}
/**
* 计算每个商品在优惠券,活动中应该分配到的优惠
*
* @param array $new_goods_list
* @param float $goods_amount
* @param float $discount_fee
* @param array $activity_discount_infos
* @param float $activity_discount_money
* @param float $dispatch_discount_money
* @param array $free_shipping_goods_ids
* @return array
*/
private static function calcDiscountFee(
$new_goods_list,
$goods_amount,
$discount_fee,
$activity_discount_infos,
$activity_discount_money
) {
if (floatval($activity_discount_money)) {
foreach ($activity_discount_infos as $key => $info) {
if ($info['activity_type'] == 'full_gift') {
// 满赠跳过
continue;
}
$current_discount_goods_ids = explode(',', $info['goods_ids']);
$current_total_goods_amount = 0; // 当前活动的商品总金额
foreach ($new_goods_list as $key => $buyinfo) {
if (in_array($buyinfo['goods_id'], $current_discount_goods_ids)) {
$current_total_goods_amount = bcadd($current_total_goods_amount, $buyinfo['goods_amount'], 2);
}
}
// 计算当前活动商品,每个商品应该分配到的优惠金额
foreach ($new_goods_list as $key => $buyinfo) {
if (!in_array($buyinfo['goods_id'], $current_discount_goods_ids)) {
// 不是当前活动的商品,跳过
continue;
}
$scale = 0; // 按照商品价格和总价计算每个 item 的比例
if (floatval($current_total_goods_amount)) { // 字符串 0.00 是 true, 这里转下类型在判断
$scale = bcdiv($buyinfo['goods_amount'], $current_total_goods_amount, 6);
}
// 每个商品分配到的折扣
$current_activity_discount_fee = round(bcmul($info['activity_discount_money'], $scale, 3), 2);
$new_goods_list[$key]['discount_fee'] = bcadd($new_goods_list[$key]['discount_fee'], $current_activity_discount_fee, 2);
}
}
}
// 剩余要加权平均分配的优惠
$last_discount_money = bcsub($discount_fee, $activity_discount_money, 2);
// 计算每个商品分配到的优惠券的优惠金额,顺便计算出来 pay_price 的金额
foreach ($new_goods_list as $key => $buyinfo) {
$scale = 0; // 按照商品价格和总价计算每个 item 的比例
if (floatval($goods_amount)) { // 字符串 0.00 是 true, 这里转下类型在判断
$scale = bcdiv($buyinfo['goods_amount'], $goods_amount, 6);
}
// 每个商品分配到的折扣
$current_discount_fee = round(bcmul($last_discount_money, $scale, 3), 2);
$new_goods_list[$key]['discount_fee'] = bcadd($new_goods_list[$key]['discount_fee'], $current_discount_fee, 2);
// 每个商品除了运费之后分配的支付金额
$new_goods_list[$key]['pay_price'] = bcsub($buyinfo['goods_amount'], $new_goods_list[$key]['discount_fee'], 2);
}
return $new_goods_list;
}
}