diff --git a/README.md b/README.md index a4d4603..a24fd6d 100644 --- a/README.md +++ b/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` ## 配置 diff --git a/composer.json b/composer.json index e6faf76..c5ef026 100644 --- a/composer.json +++ b/composer.json @@ -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/" diff --git a/database/2022_08_15_083640_create_orders_table.php b/database/2022_08_15_083640_create_orders_table.php index be2ccc8..6e889f1 100644 --- a/database/2022_08_15_083640_create_orders_table.php +++ b/database/2022_08_15_083640_create_orders_table.php @@ -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('订单-商品'); }); diff --git a/src/OrderServiceProvider.php b/src/OrderServiceProvider.php index 7c06f81..6825fcf 100644 --- a/src/OrderServiceProvider.php +++ b/src/OrderServiceProvider.php @@ -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'); } diff --git a/src/OrderStore.php b/src/OrderStore.php index 15be7fb..3c77f88 100644 --- a/src/OrderStore.php +++ b/src/OrderStore.php @@ -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); }