4
0
Fork 0
panliang 2022-09-23 16:53:25 +08:00
parent ba11205bea
commit aa923701d8
5 changed files with 130 additions and 91 deletions

View File

@ -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`
## 配置

View File

@ -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/"

View File

@ -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('订单-商品');
});

View File

@ -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');
}

View File

@ -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);
}