387 lines
8.6 KiB
PHP
387 lines
8.6 KiB
PHP
<?php
|
|
|
|
namespace Modules\Mall\Facades;
|
|
|
|
use Exception;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Modules\Mall\Events\OrderCreated;
|
|
use Modules\Mall\Models\Address;
|
|
use Modules\Mall\Models\Goods;
|
|
use Modules\Mall\Models\Order as OrderModel;
|
|
use Modules\User\Models\User;
|
|
|
|
class Order
|
|
{
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected int $user_id;
|
|
|
|
/**
|
|
* @var float
|
|
*/
|
|
protected float $total;
|
|
|
|
/**
|
|
* @var Address
|
|
*/
|
|
protected Address $address;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected Collection $items;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected string $remark;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
protected int $type;
|
|
protected int $channel = 1;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected array $source;
|
|
|
|
/**
|
|
* Notes: 设置当前用户
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/19 10:40
|
|
* @param User $user
|
|
* @return $this
|
|
*/
|
|
public function user(User $user): Order
|
|
{
|
|
$this->user_id = $user->getAuthIdentifier();;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Notes: 设置类型
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/10/18 10:03
|
|
* @param $type
|
|
* @return $this
|
|
*/
|
|
public function type($type): Order
|
|
{
|
|
$this->type = $type;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Notes: 订单渠道
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date: 2022/9/8 15:51
|
|
*/
|
|
public function channel($channel): self
|
|
{
|
|
$this->channel = $channel;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Notes: 设置订单备注信息
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/19 10:40
|
|
* @param string|null $remark
|
|
* @return $this
|
|
*/
|
|
public function remark(?string $remark = ''): Order
|
|
{
|
|
$this->remark = $remark;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Notes: 附加数据
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/19 10:40
|
|
* @param array $source
|
|
* @return $this
|
|
*/
|
|
public function source(array $source): Order
|
|
{
|
|
$this->source = $source;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Notes: 设置商品数据
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/14 13:58
|
|
* @param $items
|
|
* @return $this
|
|
*/
|
|
public function items($items): Order
|
|
{
|
|
$this->items = new Collection($items);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Notes: 设置订单收货地址
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/19 10:41
|
|
* @param Address $address
|
|
* @return $this
|
|
*/
|
|
public function address(Address $address): Order
|
|
{
|
|
$this->address = $address;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Notes: 创建订单
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/19 10:42
|
|
* @return Collection
|
|
* @throws \Exception
|
|
*/
|
|
public function create(): Collection
|
|
{
|
|
if ($this->items->isEmpty()) {
|
|
throw new Exception('无法创建无内容的订单');
|
|
}
|
|
|
|
if (! is_numeric($this->user_id)) {
|
|
throw new Exception('必须先设置订单用户');
|
|
}
|
|
$splits = $this->splitOrderByShop();
|
|
|
|
DB::beginTransaction();
|
|
|
|
try {
|
|
$orders = [];
|
|
foreach ($splits as $split) {
|
|
$orders[] = $this->createOne($split['items']);
|
|
}
|
|
|
|
DB::commit();
|
|
$result = new Collection($orders);
|
|
|
|
$result->total = $this->total();
|
|
|
|
return $result;
|
|
} catch (Exception $exception) {
|
|
DB::rollBack();
|
|
throw new Exception($exception->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Notes: 按照商户,对订单进行分组
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/19 10:43
|
|
* @return mixed
|
|
* @throws \Exception
|
|
*/
|
|
public function splitOrderByShop()
|
|
{
|
|
if (empty($this->items)) {
|
|
throw new Exception('无法创建无内容的订单');
|
|
}
|
|
|
|
return $this->items->groupBy('orderby')->map(function ($items, $key) {
|
|
/**
|
|
* 计算分订单总价格
|
|
*/
|
|
$items->amount = $items->reduce(function ($total, $item) {
|
|
return $total + $item->total();
|
|
});
|
|
|
|
/**
|
|
* 计算分订单总数量
|
|
*/
|
|
$items->qty = $items->reduce(function ($qty, $item) {
|
|
return $qty + $item->qty;
|
|
});
|
|
|
|
/**
|
|
* 计算分订单运费
|
|
*/
|
|
$items->freight = $items->reduce(function ($total, $item) {
|
|
return $total + $item->freight();
|
|
});
|
|
|
|
/**
|
|
* 回传店铺ID
|
|
*/
|
|
$items->shop_id = $items->first()->shop_id;
|
|
|
|
return [
|
|
'shop' => $items->first()->shop(),
|
|
'items' => $items,
|
|
];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Notes: 创建一条订单记录
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/21 10:24
|
|
* @param Collection $split
|
|
* @return Builder|Model
|
|
* @throws \Exception
|
|
*/
|
|
protected function createOne(Collection $split)
|
|
{
|
|
/**
|
|
* 创建主订单
|
|
*/
|
|
$order = OrderModel::query()
|
|
->create([
|
|
'shop_id' => $split->shop_id,
|
|
'user_id' => $this->user_id,
|
|
'amount' => $split->amount,
|
|
'type' => $this->type,
|
|
'channel' => $this->channel,
|
|
'freight' => $split->freight,
|
|
'remark' => $this->remark,
|
|
'source' => $this->source ?? null,
|
|
]);
|
|
|
|
/**
|
|
* 创建订单子条目
|
|
*/
|
|
foreach ($split as $item) {
|
|
// 库存校验
|
|
if ($item->qty > $item->model->getGoodsStock()) {
|
|
throw new Exception(sprintf('[%s]库存不足', $item->model->getGoodsName()));
|
|
}
|
|
|
|
$order->items()
|
|
->create([
|
|
'item_type' => get_class($item->model),
|
|
'item_id' => $item->sku_id,
|
|
'qty' => $item->qty,
|
|
'price' => $item->price,
|
|
'source' => $item->getSource(),
|
|
]);
|
|
|
|
if ($item->model->goods->deduct_stock_type == Goods::DEDUCT_STOCK_ORDER) {
|
|
// 扣减库存
|
|
$item->model->deductStock($item->qty, $order->user_id);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 自动更新地址
|
|
*/
|
|
if ($this->address instanceof Address) {
|
|
$this->setOrderAddress($order, $this->address);
|
|
}
|
|
|
|
/**
|
|
* 订单自动审核
|
|
*/
|
|
if (config('order.auto_audit')) {
|
|
$order->audit();
|
|
}
|
|
|
|
event(new OrderCreated($order));
|
|
|
|
return $order;
|
|
}
|
|
|
|
/**
|
|
* Notes: 计算订单总价格
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/21 10:26
|
|
* @return float|int|string
|
|
*/
|
|
public function total()
|
|
{
|
|
$this->total = 0;
|
|
foreach ($this->items as $item) {
|
|
$this->total = bcadd($this->total, $item->total(), 2);
|
|
}
|
|
|
|
return $this->total;
|
|
}
|
|
|
|
/**
|
|
* Notes: 设置订单收货地址
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/21 10:26
|
|
* @param $order
|
|
* @param $address
|
|
*/
|
|
protected function setOrderAddress($order, $address)
|
|
{
|
|
$order->express()
|
|
->create([
|
|
'name' => $address->name,
|
|
'mobile' => $address->mobile,
|
|
'province_id' => $address->province_id,
|
|
'city_id' => $address->city_id,
|
|
'district_id' => $address->district_id,
|
|
'address' => $address->address,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Notes: 魔术方法,获取一些参数
|
|
*
|
|
* @Author: 玄尘
|
|
* @Date : 2021/5/21 10:27
|
|
* @param $attr
|
|
* @return float|int|string|null
|
|
*/
|
|
public function __get($attr)
|
|
{
|
|
switch ($attr) {
|
|
case 'total':
|
|
return $this->total();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Notes: 获取订单详情
|
|
*
|
|
* @Author: <C.Jason>
|
|
* @Date : 2019/11/22 1:51 下午
|
|
* @param string $order_no
|
|
* @return mixed
|
|
*/
|
|
public function get(string $order_no)
|
|
{
|
|
return OrderModel::where('user_id', $this->user_id)
|
|
->with(['user', 'items', 'express', 'seller', 'logs'])
|
|
->where('order_no', $order_no)
|
|
->first();
|
|
}
|
|
|
|
}
|