master
parent
ba11205bea
commit
aa923701d8
16
README.md
16
README.md
|
|
@ -2,11 +2,21 @@
|
|||
|
||||
订单管理
|
||||
|
||||
## 依赖
|
||||
|
||||
- [peidikeji/dcat-admin-user](https://gitea.peidikeji.cn/pdkj/dcat-admin-user)
|
||||
- [peidikeji/dcat-admin-goods](https://gitea.peidikeji.cn/pdkj/dcat-admin-goods)
|
||||
|
||||
## 安装
|
||||
|
||||
- `composer config repositories.peidikeji/dcat-admin-order git git@gitea.peidikeji.cn:dcat-admin/dcat-admin-order.git`
|
||||
- `composer require peidikeji/dcat-admin-order dev-develop`
|
||||
- `php artisan vendor:publish --provider=Peidikeji\Order\OrderServiceProvider`
|
||||
- 进入项目根目录
|
||||
- `mkdir packages && cd packages`
|
||||
- `git clone https://gitea.peidikeji.cn/pdkj/dcat-admin-order.git`
|
||||
- `rm -rf dcat-admin-order/.git`
|
||||
- 返回项目根目录
|
||||
- `composer config repositories.peidikeji/dcat-admin-order path ./packages/dcat-admin-order`
|
||||
- `composer require peidikeji/dcat-admin-order:dev-develop`
|
||||
- `php artisan migrate`
|
||||
|
||||
## 配置
|
||||
|
||||
|
|
|
|||
|
|
@ -12,12 +12,6 @@
|
|||
"email": "1163816051@qq.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0.2",
|
||||
"peidikeji/dcat-admin": "*",
|
||||
"laravel/framework": "^9.0",
|
||||
"peidikeji/dcat-admin-user": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Peidikeji\\Order\\": "src/"
|
||||
|
|
|
|||
|
|
@ -55,14 +55,12 @@ return new class extends Migration
|
|||
Schema::create('order_goods', function (Blueprint $table) {
|
||||
$table->id('id');
|
||||
$table->unsignedBigInteger('order_id')->comment('订单ID, orders.id');
|
||||
$table->unsignedBigInteger('merchant_id')->nullable()->comment('商户ID');
|
||||
|
||||
$table->unsignedBigInteger('goods_id')->comment('所属商品, 关联 goods.id');
|
||||
$table->string('goods_name');
|
||||
$table->string('goods_sn')->nullable();
|
||||
$table->string('cover_image')->nullable()->comment('封面图');
|
||||
$table->string('goods_sn')->nullable();
|
||||
$table->unsignedBigInteger('goods_id')->nullable()->comment('所属商品, 关联 goods.id');
|
||||
$table->decimal('price', 12, 2)->comment('售价');
|
||||
$table->decimal('score_max_amount', 12, 2)->default(0)->comment('积分抵扣最大值');
|
||||
|
||||
$table->string('goods_sku_sn')->nullable();
|
||||
$table->unsignedBigInteger('goods_sku_id')->nullable()->comment('所属商品SKU, 关联 goods_skus.id');
|
||||
|
|
@ -76,10 +74,6 @@ return new class extends Migration
|
|||
|
||||
$table->unsignedInteger('ship_amount')->default(0)->comment('累计发货数量');
|
||||
|
||||
$table->unsignedBigInteger('shipping_tmp_id')->nullable()->comment('配送模板id');
|
||||
$table->decimal('weight', 12, 2)->nullable()->comment('重量');
|
||||
$table->decimal('volume', 12, 2)->nullable()->comment('体积');
|
||||
|
||||
$table->comment('订单-商品');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class OrderServiceProvider extends ServiceProvider
|
|||
__DIR__.'/../database/' => database_path('migrations')
|
||||
], 'dcat-admin-order-migrations');
|
||||
|
||||
// $this->loadMigrationsFrom(__DIR__.'/../database/');
|
||||
$this->loadMigrationsFrom(__DIR__.'/../database/');
|
||||
|
||||
$this->loadTranslationsFrom(__DIR__.'/../lang', 'dcat-admin-order');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace Peidikeji\Order;
|
||||
|
||||
use App\Services\ShippingMoneyService;
|
||||
use Peidikeji\Coupon\CouponService;
|
||||
use Peidikeji\Coupon\Models\UserCoupon;
|
||||
use Peidikeji\Goods\Models\Goods;
|
||||
use Peidikeji\Order\Enums\OrderScene;
|
||||
use Peidikeji\Order\Enums\ShipWay;
|
||||
use Peidikeji\Order\Events\OrderCreated;
|
||||
use Peidikeji\Order\Exceptions\OrderException;
|
||||
use Peidikeji\Order\Models\Order;
|
||||
|
|
@ -36,41 +36,44 @@ class OrderStore
|
|||
|
||||
// amount: 使用积分, money: 积分抵扣金额, ratio: 抵扣比例
|
||||
public $score = ['amount' => 0, 'money' => 0, 'ratio' => 0];
|
||||
|
||||
|
||||
// is: 是否Vip, money: 会员累计优惠金额
|
||||
public $vip = ['is' => false, 'money' => 0];
|
||||
|
||||
// address: 配送地址, money: 配送费, way: 配送方式
|
||||
public $ship = ['address' => null, 'money' => 0, 'way' => null];
|
||||
|
||||
// 使用优惠券
|
||||
public $coupon = ['id' => null, 'money' => 0];
|
||||
|
||||
public function __construct($scene = null)
|
||||
{
|
||||
$this->scene = $scene;
|
||||
$this->orderGoodsList = collect();
|
||||
}
|
||||
|
||||
public function scene($scene)
|
||||
public function scene($scene): static
|
||||
{
|
||||
$this->scene = $scene;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function user($user)
|
||||
public function user($user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function merchant($merchant)
|
||||
public function merchant($merchant): static
|
||||
{
|
||||
$this->merchant = $merchant;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function remarks($remarks)
|
||||
public function remarks($remarks): static
|
||||
{
|
||||
$this->remarks = $remarks;
|
||||
|
||||
|
|
@ -79,24 +82,34 @@ class OrderStore
|
|||
|
||||
/**
|
||||
* 购买商品
|
||||
*
|
||||
* @param array $goods 商品参数 [{id, amount, spec: [{name, value}]}]
|
||||
* @param bool $vip 是否使用Vip价格
|
||||
*
|
||||
* @param array $params 商品参数 [{id, amount, spec: [{name, value}]}]
|
||||
* @param mixed $vip 是否使用Vip价格(null: 根据用户判断, bool: 是否强制使用)
|
||||
* @throws OrderException
|
||||
*/
|
||||
public function goods($params, $vip = null)
|
||||
public function goods(array $params, mixed $vip = null): static
|
||||
{
|
||||
$merchant = $this->merchant;
|
||||
$query = Goods::show()->whereIn('id', array_column($params, 'id'));
|
||||
$goodsList = null;
|
||||
if ($this->scene === OrderScene::Online->value) {
|
||||
$query->whereNull('merchant_id');
|
||||
$goodsList = Goods::show()->whereNull('merchant_id')->whereIn('id', array_column($params, 'id'))->get();
|
||||
} elseif ($this->scene === OrderScene::Merchant->value) {
|
||||
$goodsList = Goods::show()->where('merchant_id', $merchant->id)->whereIn('id', array_column($params, 'id'))->get();
|
||||
} elseif ($this->scene === OrderScene::Scan->value) {
|
||||
$money = $this->money;
|
||||
$goodsList = collect()->push([
|
||||
'id' => data_get($params, '0.id'),
|
||||
'name' => $merchant->name,
|
||||
'merchant_id' => $merchant->id,
|
||||
'goods_name' => '线下交易',
|
||||
'cover_image' => $merchant->logo,
|
||||
'price' => $money,
|
||||
'vip_price' => $money,
|
||||
'amount' => 1,
|
||||
'on_sale' => 1,
|
||||
'stock' => 1
|
||||
]);
|
||||
}
|
||||
else if ($this->scene === OrderScene::Merchant->value || $this->scene === OrderScene::Scan->value) {
|
||||
if (!$merchant) {
|
||||
throw new OrderException('请选择店铺');
|
||||
}
|
||||
$query->where('merchant_id', $merchant->id);
|
||||
}
|
||||
$goodsList = $query->get();
|
||||
if ($goodsList->count() === 0) {
|
||||
throw new OrderException('请选择有效商品');
|
||||
}
|
||||
|
|
@ -106,34 +119,40 @@ class OrderStore
|
|||
if ($vip === null && $user) {
|
||||
$vip = $user->isVip();
|
||||
}
|
||||
foreach($params as $item) {
|
||||
foreach ($params as $item) {
|
||||
$goods = $goodsList->firstWhere('id', $item['id']);
|
||||
if (!$goods->on_sale) {
|
||||
throw new OrderException($goods->name . ' 未上架');
|
||||
if (! data_get($goods, 'on_sale')) {
|
||||
throw new OrderException($goods->name.' 未上架');
|
||||
}
|
||||
$stock = $goods->stock;
|
||||
|
||||
// 最大抵扣积分
|
||||
$scoreRatio = data_get($goods, 'score_max_amount', $merchant->score_max_ratio);
|
||||
$price = data_get($goods, $vip ? 'vip_price' : 'price');
|
||||
$scoreMaxAmount = $price * $scoreRatio / 100 / Setting::where('slug', 'discount_profit_ratio')->value('value');
|
||||
|
||||
$stock = data_get($goods, 'stock');
|
||||
$orderGoods = [
|
||||
'goods_id' => $goods->id,
|
||||
'merchant_id' => $goods->merchant_id,
|
||||
'goods_name' => $goods->name,
|
||||
'goods_sn' => $goods->sn,
|
||||
'cover_image' => $goods->cover_image,
|
||||
'price' => $goods->price,
|
||||
'vip_price' => $goods->vip_price,
|
||||
'score_max_amount' => $goods->score_max_amount,
|
||||
'attr' => $goods->attr,
|
||||
'goods_id' => data_get($goods, 'id'),
|
||||
'merchant_id' => data_get($goods, 'merchant_id'),
|
||||
'goods_name' => data_get($goods, 'name'),
|
||||
'goods_sn' => data_get($goods, 'sn'),
|
||||
'cover_image' => data_get($goods, 'cover_image'),
|
||||
'price' => data_get($goods, 'price'),
|
||||
'vip_price' => data_get($goods, 'vip_price'),
|
||||
'score_max_amount' => $scoreMaxAmount,
|
||||
'attr' => data_get($goods, 'attr'),
|
||||
'spec' => data_get($item, 'spec'),
|
||||
'part' => data_get($item, 'part'),
|
||||
'amount' => data_get($item, 'amount', 1),
|
||||
'money' => 0,
|
||||
'shipping_tmp_id' => $goods->shipping_tmp_id,
|
||||
'weight' => $goods->weight,
|
||||
'volume' => $goods->volume,
|
||||
'shipping_tmp_id' => data_get($goods, 'shipping_tmp_id'),
|
||||
'weight' => data_get($goods, 'weight'),
|
||||
'volume' => data_get($goods, 'volume'),
|
||||
];
|
||||
if (isset($item['spec']) && $item['spec']) {
|
||||
if (data_get($item, 'spec')) {
|
||||
$sku = $goods->skus()->jsonArray($item['spec'])->first();
|
||||
if (!$sku) {
|
||||
throw new OrderException($goods->name . ' 规格不存在');
|
||||
if (! $sku) {
|
||||
throw new OrderException($goods->name.' 规格不存在');
|
||||
}
|
||||
$stock = $sku->stock;
|
||||
$orderGoods['goods_sku_id'] = $sku->id;
|
||||
|
|
@ -146,7 +165,7 @@ class OrderStore
|
|||
}
|
||||
|
||||
if ($orderGoods['amount'] > $stock) {
|
||||
throw new OrderException($goods->name . ' 库存不足');
|
||||
throw new OrderException($goods->name.' 库存不足');
|
||||
}
|
||||
|
||||
$orderGoods['money'] = round($orderGoods['price'] * $orderGoods['amount'], 2, PHP_ROUND_HALF_DOWN);
|
||||
|
|
@ -157,40 +176,49 @@ class OrderStore
|
|||
$this->goodsList = $goodsList;
|
||||
$this->orderGoodsList = $orderGoodsList;
|
||||
$this->money = $orderGoodsList->sum('money');
|
||||
$this->vip = ['is' => $vip, 'money' => $vip ? $orderGoodsList->sum(fn($item) => $item['price'] - $item['vip_price']) : 0];
|
||||
$this->vip = ['is' => $vip, 'money' => $vip ? $orderGoodsList->sum(fn ($item) => $item['price'] - $item['vip_price']) : 0];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配送
|
||||
*
|
||||
* @param string $way 配送方式
|
||||
* @param array $address 地址 ['name' => '收货人', 'phone' => '18223350967', 'region' => ['重庆市', '重庆市', '渝北区'], 'zone' => [2221, 2222, 2234], 'address' => '线外城市花园3栋20楼'];
|
||||
*
|
||||
* @param string $way 配送方式
|
||||
* @param array $address 地址 ['name' => '收货人', 'phone' => '18223350967', 'region' => ['重庆市', '重庆市', '渝北区'], 'zone' => [2221, 2222, 2234], 'address' => '线外城市花园3栋20楼'];
|
||||
*/
|
||||
public function ship($way, $address = null)
|
||||
{
|
||||
$money = 0;
|
||||
if ($way === ShipWay::Express->value && $address) {
|
||||
$shipService = new ShippingMoneyService();
|
||||
$money = $shipService->getShippingMoney($this->orderGoodsList->map(fn($item) => [
|
||||
'shipping_tmp_id' => $item['shipping_tmp_id'],
|
||||
'weight' => $item['weight'],
|
||||
'volume' => $item['volume'],
|
||||
'num' => $item['amount'],
|
||||
'price' => $item['price'],
|
||||
]), data_get($address, 'zone.2'));
|
||||
}
|
||||
$this->ship = ['way' => $way, 'address' => $address, 'money' => $money];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用优惠券
|
||||
*
|
||||
* @param UserCoupon $info 优惠券领取记录
|
||||
*/
|
||||
public function coupon(UserCoupon $info)
|
||||
{
|
||||
$info->canUse(true);
|
||||
$couponMoney = CouponService::make()->available($info, $this->orderGoodsList);
|
||||
if ($couponMoney === false) {
|
||||
throw new OrderException('优惠券不可用');
|
||||
}
|
||||
|
||||
$this->coupon = ['id' => $info->id, 'money' => $couponMoney];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置订单总金额
|
||||
*/
|
||||
public function money($money)
|
||||
{
|
||||
if ($this->scene !== OrderScene::Scan->value) {
|
||||
throw new OrderException('非扫码付款, 无法设置订单金额');
|
||||
}
|
||||
$this->money = $money;
|
||||
|
||||
return $this;
|
||||
|
|
@ -198,12 +226,13 @@ class OrderStore
|
|||
|
||||
/**
|
||||
* 使用积分抵扣
|
||||
*
|
||||
* @param float $amount 积分数量
|
||||
* @param float $ratio 抵扣比例(0-1)
|
||||
* @param float $money 金额(默认 = $amount * $ratio)
|
||||
*
|
||||
* @param int $amount 积分数量
|
||||
* @param float|null $ratio 抵扣比例(0-1)
|
||||
* @param int|null $money 金额(默认 = $amount * $ratio)
|
||||
* @throws OrderException
|
||||
*/
|
||||
public function score($amount, $ratio = null, $money = null)
|
||||
public function score(int $amount, float $ratio = null, int $money = null): static
|
||||
{
|
||||
$user = $this->user;
|
||||
if ($amount > 0 && $user) {
|
||||
|
|
@ -225,7 +254,7 @@ class OrderStore
|
|||
if ($money === null) {
|
||||
$money = $amount * $ratio;
|
||||
}
|
||||
$money = round($money, 2, PHP_ROUND_HALF_DOWN);
|
||||
$money = floor($money);
|
||||
|
||||
$this->score = compact('money', 'amount', 'ratio');
|
||||
}
|
||||
|
|
@ -235,15 +264,16 @@ class OrderStore
|
|||
|
||||
/**
|
||||
* 生成订单
|
||||
*
|
||||
*
|
||||
* @return Order
|
||||
* @throws OrderException
|
||||
*/
|
||||
public function create()
|
||||
public function create(): Order
|
||||
{
|
||||
$order = new Order([
|
||||
'sn' => OrderService::make()->generateSn(),
|
||||
]);
|
||||
|
||||
|
||||
$order->scene = $this->scene;
|
||||
$order->user_id = $this->user?->id;
|
||||
$order->merchant_id = $this->merchant?->id;
|
||||
|
|
@ -257,6 +287,9 @@ class OrderStore
|
|||
$order->score_discount_ratio = $this->score['ratio'];
|
||||
$order->score_discount_money = $this->score['money'];
|
||||
|
||||
$order->coupon_money = $this->coupon['money'];
|
||||
$order->coupon_id = $this->coupon['id'];
|
||||
|
||||
$order->vip_discount_money = $this->vip['money'];
|
||||
|
||||
$order->pay_money = $this->getPayMoney();
|
||||
|
|
@ -272,7 +305,7 @@ class OrderStore
|
|||
|
||||
/**
|
||||
* 获取付款金额
|
||||
*
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getPayMoney()
|
||||
|
|
@ -285,31 +318,39 @@ class OrderStore
|
|||
// Vip 优惠
|
||||
$money -= $this->vip['money'];
|
||||
|
||||
// 优惠券抵扣
|
||||
$money -= $this->coupon['money'];
|
||||
|
||||
// 配送费
|
||||
$money += $this->ship['money'];
|
||||
|
||||
return $money;
|
||||
return max($money, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户应得奖励
|
||||
*
|
||||
* @param float $payMoney 支付金额
|
||||
*
|
||||
* @param float|null $payMoney 支付金额
|
||||
* @return float
|
||||
* @throws OrderException
|
||||
*/
|
||||
public function getUserBonus($payMoney = null)
|
||||
public function getUserBonus(float $payMoney = null): float
|
||||
{
|
||||
$money = 0;
|
||||
|
||||
if ($this->scene === OrderScene::Merchant->value && $this->merchant) {
|
||||
if ($this->merchant) {
|
||||
$payMoney = is_null($payMoney) ? $this->getPayMoney() : $payMoney;
|
||||
$money = round($payMoney * ($this->merchant->profit_ratio / 100), 2, PHP_ROUND_HALF_DOWN);
|
||||
$money = floor($payMoney * ($this->merchant->profit_ratio / 100));
|
||||
$merchantUser = $this->merchant->user;
|
||||
if ($merchantUser->profit < $money) {
|
||||
throw new OrderException('店铺返现账户余额不足');
|
||||
}
|
||||
}
|
||||
|
||||
return $money;
|
||||
}
|
||||
|
||||
public static function init(...$params)
|
||||
|
||||
public static function init(...$params): static
|
||||
{
|
||||
return new static(...$params);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue