diff --git a/app/Admin/Controllers/AfterSaleController.php b/app/Admin/Controllers/AfterSaleController.php index b8951156..3865ad51 100644 --- a/app/Admin/Controllers/AfterSaleController.php +++ b/app/Admin/Controllers/AfterSaleController.php @@ -79,6 +79,7 @@ class AfterSaleController extends AdminController } return bcdiv($value, 100, 2); })->prepend('¥'); + $grid->column('points'); $grid->column('type')->using([ AfterSaleModel::TYPE_REFUND_AND_RETURN => '退款退货', AfterSaleModel::TYPE_REFUND => '退款', @@ -158,6 +159,7 @@ class AfterSaleController extends AdminController $show->field('amount', '售后金额')->as(function ($amount) { return '¥'.bcdiv($amount, 100, 2); }); + $show->field('points', '售后积分'); } $show->width(6)->field('user.user_info.nickname', '下单用户'); $show->field('user.phone'); diff --git a/app/Admin/Forms/AfterSaleVerify.php b/app/Admin/Forms/AfterSaleVerify.php index 9e6c1afe..11bf2285 100644 --- a/app/Admin/Forms/AfterSaleVerify.php +++ b/app/Admin/Forms/AfterSaleVerify.php @@ -66,13 +66,15 @@ class AfterSaleVerify extends Form implements LazyRenderable } //调整换货,补货时amount问题 $amount = (int) Arr::get($input, 'amount', 0) ?: $afterSale->amount; + $points = (int) Arr::get($input, 'points', 0) ?: $afterSale->points; if (in_array($afterSale->type, [Aftersale::TYPE_CHANGE, Aftersale::TYPE_FILL])) { //如果调整数量, 则同时调整价格 if ($num != $afterSale->num) { $amount = bcmul(bcdiv($afterSale->amount, $afterSale->num), $num); + $points = bcmul(bcdiv($afterSale->points, $afterSale->num), $num); } } - $afterSaleService->verify($afterSale, $input['remarks3'], $amount, $num); + $afterSaleService->verify($afterSale, $input['remarks3'], $amount, $points, $num); } elseif ($input['state'] == 1) {//需要补充资料 $afterSaleService->backApply($afterSale, $input['remarks1']); } @@ -103,6 +105,7 @@ class AfterSaleVerify extends Form implements LazyRenderable })->saving(function ($amount) { return bcmul($amount, 100); })->symbol('¥'); + $this->number('points')->default($afterSale->points); } $this->radio('state') diff --git a/app/Endpoint/Api/Http/Controllers/Order/OrderController.php b/app/Endpoint/Api/Http/Controllers/Order/OrderController.php index 34f8bf4f..d84af76f 100644 --- a/app/Endpoint/Api/Http/Controllers/Order/OrderController.php +++ b/app/Endpoint/Api/Http/Controllers/Order/OrderController.php @@ -50,6 +50,7 @@ class OrderController extends Controller 'shipping_address_id' => ['bail', 'nullable', 'int'], 'coupon_id' => ['bail', 'nullable', 'int'], 'note' => ['bail', 'nullable', 'string', 'max:255'], + 'points' => ['bail', 'nullable', 'int'], ], match (true) { $request->filled('order_pre') => [ 'order_pre' => ['bail','required', 'int'], @@ -72,8 +73,9 @@ class OrderController extends Controller 'shipping_address_id' => '收货地址', 'coupon_id' => '优惠券', 'note' => '订单备注', + 'points' => '积分', ]); - + $user = $request->user(); try { @@ -89,9 +91,16 @@ class OrderController extends Controller 'desk' => $request->input('desk'), 'note' => $request->input('note'), 'coupon_id' => $request->input('coupon_id'), - ] + 'points' => $request->input('points', 0), + ], + ), + $request->filled('order_pre') => $orderService->createOrderByPre( + $user, + OrderPre::findOrFail($request->input('order_pre')), + $request->input('coupon_id'), + $request->input('note'), + $request->input('points', 0), ), - $request->filled('order_pre') => $orderService->createOrderByPre($user, OrderPre::findOrFail($request->input('order_pre')), $request->input('coupon_id'), $request->input('note')), $request->filled('product') => $orderService->createQuickOrder( $user, $validated['product']['sku_id'], @@ -99,6 +108,7 @@ class OrderController extends Controller $validated['shipping_address_id'] ?? null, $validated['coupon_id'] ?? null, $validated['note'] ?? null, + $request->input('points', 0), ), default => $orderService->createShoppingCartOrder( $user, @@ -106,6 +116,7 @@ class OrderController extends Controller $validated['shipping_address_id'] ?? null, $validated['coupon_id'] ?? null, $validated['note'] ?? null, + $request->input('points', 0), ), }; }); diff --git a/app/Endpoint/Api/Http/Resources/AfterSaleResource.php b/app/Endpoint/Api/Http/Resources/AfterSaleResource.php index 4b1edcad..ac08d2b4 100644 --- a/app/Endpoint/Api/Http/Resources/AfterSaleResource.php +++ b/app/Endpoint/Api/Http/Resources/AfterSaleResource.php @@ -21,7 +21,7 @@ class AfterSaleResource extends JsonResource 'product'=>[ 'name' => $this->orderProduct->name, 'cover' => $this->orderProduct->cover, - 'sell_price' => bcdiv($this->orderProduct->total_amount, $this->orderProduct->quantity * 100, 2), + 'sell_price' => bcdiv($this->orderProduct->sell_price, 100, 2), 'num'=> $this->num, 'is_gift' => $this->orderProduct->isGift(), 'total_amount' => $this->orderProduct->total_amount, @@ -33,6 +33,7 @@ class AfterSaleResource extends JsonResource 'remarks' => $this->remarks, 'created_at' => $this->created_at->toDateTimeString(), 'amount'=> $this->amount_format, + 'points' => $this->points, // 'logs' => AfterSaleLogResource::make($this->whenLoaded('logs')), ]; } diff --git a/app/Endpoint/Api/Http/Resources/OrderResource.php b/app/Endpoint/Api/Http/Resources/OrderResource.php index 4694fb95..27b7b7b6 100644 --- a/app/Endpoint/Api/Http/Resources/OrderResource.php +++ b/app/Endpoint/Api/Http/Resources/OrderResource.php @@ -30,6 +30,7 @@ class OrderResource extends JsonResource 'bargain_amount' => $this->bargain_amount_format, 'shipping_fee' => $this->shipping_fee_format, 'products_total_amount' => $this->products_total_amount_format, + 'point_discount_amount' => $this->point_discount_amount_format, 'total_amount' => $this->total_amount_format, 'status' => $this->order_status, 'note' => (string) $this->note, diff --git a/app/Enums/PointLogAction.php b/app/Enums/PointLogAction.php index 702b1b16..1885b054 100644 --- a/app/Enums/PointLogAction.php +++ b/app/Enums/PointLogAction.php @@ -8,6 +8,7 @@ enum PointLogAction: int { case Recharge = 1; case Deduction = 2; case Consumption = 3; + case Refund = 4; public function label(): string { @@ -20,6 +21,7 @@ enum PointLogAction: int { self::Recharge->value => '充值', self::Deduction->value => '扣减', self::Consumption->value => '消费', + self::Refund->value => '退还', ]; } } diff --git a/app/Models/AfterSale.php b/app/Models/AfterSale.php index d21ea16f..138d9e27 100644 --- a/app/Models/AfterSale.php +++ b/app/Models/AfterSale.php @@ -30,6 +30,10 @@ class AfterSale extends Model 'images' => JsonArray::class, ]; + protected $attributes = [ + 'points' => 0, + ]; + protected $fillable = [ 'user_id', 'order_id', @@ -43,6 +47,7 @@ class AfterSale extends Model 'remarks', 'tracking_number', 'sales_value', + 'points', ]; public static $stateText = [ diff --git a/app/Models/Order.php b/app/Models/Order.php index 79660f17..969e4725 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -4,10 +4,10 @@ namespace App\Models; use App\Constants\OrderStatus; use App\Enums\PayWay; +use App\Models\Store\Store; use Dcat\Admin\Traits\HasDateTimeFormatter; use EloquentFilter\Filterable; use Illuminate\Database\Eloquent\Model; -use App\Models\Store\Store; class Order extends Model { @@ -45,6 +45,7 @@ class Order extends Model 'reduced_amount' => 0, 'is_change' => false, 'status' => self::STATUS_PENDING, + 'point_discount_amount' => 0, ]; /** @@ -98,6 +99,7 @@ class Order extends Model 'market_price', 'cost_price', 'profit_paid', + 'point_discount_amount', ]; /** @@ -459,4 +461,9 @@ class Order extends Model { return trim_trailing_zeros(bcdiv($this->attributes['bargain_amount'], 100, 2)); } + + public function getPointDiscountAmountFormatAttribute() + { + return trim_trailing_zeros(bcdiv($this->attributes['point_discount_amount'], 100, 2)); + } } diff --git a/app/Models/OrderProduct.php b/app/Models/OrderProduct.php index f590d417..1b994f53 100644 --- a/app/Models/OrderProduct.php +++ b/app/Models/OrderProduct.php @@ -9,6 +9,10 @@ class OrderProduct extends Model /** * @var array */ + protected $attributes = [ + 'point_discount_amount' => 0, + ]; + protected $casts = [ 'specs' => 'json', 'is_gift'=>'boolean', @@ -44,6 +48,8 @@ class OrderProduct extends Model 'bargain_amount', 'market_price', 'cost_price', + 'spent_points', + 'point_discount_amount', ]; public function packageProducts() diff --git a/app/Services/AfterSaleService.php b/app/Services/AfterSaleService.php index 08bf4a16..2418da9a 100644 --- a/app/Services/AfterSaleService.php +++ b/app/Services/AfterSaleService.php @@ -3,6 +3,7 @@ namespace App\Services; use App\Admin\Services\OrderService; +use App\Enums\PointLogAction; use App\Exceptions\BizException; use App\Models\AfterSale; use App\Models\AfterSaleLog; @@ -42,8 +43,9 @@ class AfterSaleService } $amount = ($num == $orderProduct->quantity) ? $orderProduct->total_amount : bcmul(bcdiv($orderProduct->total_amount, $orderProduct->quantity), $num); + $points = ($num == $orderProduct->quantity) ? $orderProduct->point_discount_amount : bcmul(bcdiv($orderProduct->point_discount_amount, $orderProduct->quantity), $num); - if ($amount == 0 && in_array($params['type'], [AfterSale::TYPE_REFUND])) { + if (($amount == 0 && $points == 0) && in_array($params['type'], [AfterSale::TYPE_REFUND])) { throw new BizException('实际支付金额为0的商品无法发起退款售后'); } @@ -51,6 +53,7 @@ class AfterSaleService 'user_id' => $user->id, 'order_id' => $orderProduct->order_id, 'amount' => $amount, + 'points' => $points, 'state' => AfterSale::STATE_VERIFY, 'sales_value' => $orderProduct->sales_value, ])); @@ -150,7 +153,7 @@ class AfterSaleService * @param string $remarks * @return void */ - public function verify(AfterSale $afterSale, string $remarks, int $amount, ?int $num = null) + public function verify(AfterSale $afterSale, string $remarks, int $amount, int $points, ?int $num = null) { if ($this->isWaitVerify($afterSale)) { switch ($afterSale->type) { @@ -158,6 +161,7 @@ class AfterSaleService $afterSale->update([ 'num' => $num, 'amount' => $amount, + 'points' => $points, 'state' => $afterSale::STATE_AGREE, 'remarks' => $remarks, ]); @@ -171,6 +175,7 @@ class AfterSaleService $afterSale->update([ 'num' => $num, 'amount' => $amount, + 'points' => $points, 'state' => $afterSale::STATE_AGREE, 'remarks' => $remarks, ]); @@ -184,6 +189,7 @@ class AfterSaleService $afterSale->update([ 'num' => $num, 'amount' => $amount, + 'points' => $points, 'state' => $afterSale::STATE_AGREE, 'remarks' => $remarks, ]); @@ -197,6 +203,7 @@ class AfterSaleService $afterSale->update([ 'num' => $num, 'amount' => $amount, + 'points' => $points, 'state' => $afterSale::STATE_AGREE, 'remarks' => $remarks, ]); @@ -390,6 +397,10 @@ class AfterSaleService $salesValue = bcmul($afterSaleProduct->sales_value, $qty, 2); } + if ($afterSale->points > 0) { + (new PointService())->change($order->user, $afterSale->points, PointLogAction::Refund, "售后单{$afterSale->sn}退还积分", $afterSale); + } + //执行实际退款操作; if ($afterSale->amount > 0) {//退款金额大于0才做实际退款 $order->refundLogs()->create([ diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 36fc7008..38ff3e51 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -6,6 +6,7 @@ use App\Endpoint\Api\Http\Resources\ProductSkuSimpleResource; use App\Endpoint\Api\Http\Resources\ShippingAddressResource; use App\Endpoint\Api\Http\Resources\UserCouponResource; use App\Enums\PayWay; +use App\Enums\PointLogAction; use App\Enums\SocialiteType; use App\Enums\WxpayTradeType; use App\Exceptions\BizException; @@ -20,9 +21,9 @@ use App\Models\ProductPartSku; use App\Models\ProductSku; use App\Models\ShippingAddress; use App\Models\SocialiteUser; -use App\Models\{User, OrderPre, Tag}; use App\Models\Store\{Store, Desk, DeviceRecord}; use App\Models\UserCoupon; +use App\Models\{User, OrderPre, Tag}; use App\Services\Payment\WxpayService; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\QueryException; @@ -49,6 +50,7 @@ class OrderService ?int $shippingAddressId, ?int $couponId = null, ?string $note = null, + ?int $points = null, ?BargainOrder $bargainOrder = null, ): Order { $sku = ProductSku::online()->findOrFail($skuId); @@ -58,7 +60,7 @@ class OrderService 'quantity' => $quantity, ]; - return $this->createOrder($user, [$product], $shippingAddressId, $couponId, $note, $bargainOrder); + return $this->createOrder($user, [$product], $shippingAddressId, $couponId, $note, $points, $bargainOrder); } /** @@ -78,13 +80,15 @@ class OrderService int $shippingAddressId, ?int $couponId = null, ?string $note = null, + ?int $points = null, ): Order { $order = $this->createOrder( $user, $this->getProductsByShoppingCart($user, $shoppingCartItemIds), $shippingAddressId, $couponId, - $note + $note, + $points, ); $user->shoppingCartItems()->whereIn('id', $shoppingCartItemIds)->delete(); @@ -109,6 +113,7 @@ class OrderService ?int $shippingAddressId, ?int $couponId = null, ?string $note = null, + ?int $points = null, ?BargainOrder $bargainOrder = null, ): Order { foreach ($products as $product) { @@ -148,12 +153,13 @@ class OrderService $couponDiscountAmount, $vipDiscountAmount, $shippingFee, + $points, $salesValue, $shippingAddress, $note, $coupon, $bargainOrder,//添加砍价订单逻辑 - $mapProducts + $mapProducts, ); $this->storeOrderProducts($order, $mapProducts); @@ -173,7 +179,7 @@ class OrderService /** * 预订单, 扫码下单 */ - public function createOrderByPre(User $user, OrderPre $order_pre, $coupon_id = null, $note = null) + public function createOrderByPre(User $user, OrderPre $order_pre, $coupon_id = null, $note = null, int $points = 0) { $products = []; foreach($order_pre->products as $item) { @@ -208,6 +214,7 @@ class OrderService $couponDiscountAmount, $vipDiscountAmount, 0, + $points, $salesValue, null, $note, @@ -303,6 +310,7 @@ class OrderService $couponDiscountAmount, $vipDiscountAmount, 0, + $params['points'] ?? 0, $salesValue, null, $note, @@ -352,13 +360,17 @@ class OrderService int $couponDiscountAmount, int $vipDiscountAmount, int $shippingFee, + int $points, $salesValue, ?ShippingAddress $shippingAddress, ?string $note = null, ?UserCoupon $coupon = null, ?BargainOrder $bargainOrder = null, - ?array $mapProducts = null + ?array $mapProducts = null, ): Order { + // 积分抵扣金额(1积分等于1分钱) + $pointDiscountAmount = $points; + // 订单支付金额=商品总额-券折扣金额-会员折扣金额+邮费-砍价金额 $totalAmount = $productsTotalAmount - $couponDiscountAmount - $vipDiscountAmount; @@ -372,6 +384,7 @@ class OrderService } $totalAmount += $shippingFee; + $totalAmount -= $pointDiscountAmount; // 生成不重复的订单号 do { @@ -406,9 +419,18 @@ class OrderService 'bargain_amount'=>$bargainOrder?->bargain_price ?? 0, 'market_price' => $market_price, 'cost_price' => $cost_price, + // 积分抵扣金额 + 'point_discount_amount' => $pointDiscountAmount, ]; - return $user->orders()->create($attrs); + $order = $user->orders()->create($attrs); + + // 扣除积分 + if ($points > 0) { + (new PointService)->change($user, -$points, PointLogAction::Consumption, "订单{$order->sn}使用积分", $order); + } + + return $order; } /** @@ -420,15 +442,24 @@ class OrderService */ public function storeOrderProducts(Order $order, array $mapProducts) { + // 积分抵扣金额 + $remainingPointDiscountAmount = $order->point_discount_amount; + $orderProducts = []; foreach ($mapProducts as $product) { $sku = $product['sku']; $qty = $product['quantity']; - // 支付金额 = 商品总额 - 优惠券折扣金额- 会员折扣金额 - 砍价金额 + // 支付金额 = 商品总额 - 优惠券折扣金额 - 会员折扣金额 - 砍价金额 $totalAmount = $product['total_amount'] - $product['coupon_discount_amount'] - $product['vip_discount_amount'] - $product['bargain_amount']; + // 积分抵扣金额 + $pointDiscountAmount = $totalAmount > $remainingPointDiscountAmount ? $remainingPointDiscountAmount : $totalAmount; + + $remainingPointDiscountAmount -= $pointDiscountAmount; + $totalAmount -= $pointDiscountAmount; + $orderProducts[] = [ 'gift_for_sku_id' => null, 'activity_id'=>null, @@ -451,6 +482,7 @@ class OrderService 'remain_quantity' => $qty, // 剩余发货数量 'coupon_discount_amount' => $product['coupon_discount_amount'], 'vip_discount_amount' => $product['vip_discount_amount'], + 'point_discount_amount' => $pointDiscountAmount, 'total_amount' => $totalAmount, 'bargain_amount'=> $product['bargain_amount'], 'created_at' => $order->created_at, @@ -746,8 +778,13 @@ class OrderService * @param int|null $bargainOrderId * @return array */ - public function verifyOrder(User $user, array $products, ?int $shippingAddressId = null, ?int $couponId = null, ?int $bargainOrderId = null): array - { + public function verifyOrder( + User $user, + array $products, + ?int $shippingAddressId = null, + ?int $couponId = null, + ?int $bargainOrderId = null, + ): array { // 获取收货地址 $shippingAddress = $this->getShippingAddress($user, $shippingAddressId); @@ -795,6 +832,12 @@ class OrderService $totalAmount += $shippingFee; + //--------------------------------------- + // 积分当钱花 + //--------------------------------------- + $remainingPoints = $user->userInfo->points; // 用户剩余积分 + $availablePoints = $totalAmount > $remainingPoints ? $remainingPoints : $totalAmount; // 可用积分 + return [ 'products' => collect($mapProducts)->map(function ($item) { return [ @@ -811,6 +854,9 @@ class OrderService 'coupon_discount_amount' => trim_trailing_zeros(bcdiv($couponDiscountAmount, 100, 2)), // 优惠券折扣金额 'total_amount' => trim_trailing_zeros(bcdiv($totalAmount, 100, 2)), // 实付金额 'bargain_amount' => trim_trailing_zeros(bcdiv($bargainAmount, 100, 2)), //砍价金额 + 'remaining_points' => $remainingPoints, // 剩余积分 + 'available_points' => $availablePoints, // 可用积分 + 'point_discount_amount' => trim_trailing_zeros(bcdiv($availablePoints, 100, 2)), // 积分抵扣金额 ]; } @@ -1355,6 +1401,10 @@ class OrderService ]); } + if ($order->point_discount_amount > 0) { + (new PointService())->change($order->user, $order->point_discount_amount, PointLogAction::Refund, "订单{$order->sn}退还积分", $order); + } + $order->update([ 'status' => Order::STATUS_CANCELLED, ]); diff --git a/database/migrations/2023_10_13_205858_add_point_discount_amount_to_orders_table.php b/database/migrations/2023_10_13_205858_add_point_discount_amount_to_orders_table.php new file mode 100644 index 00000000..02346c8d --- /dev/null +++ b/database/migrations/2023_10_13_205858_add_point_discount_amount_to_orders_table.php @@ -0,0 +1,32 @@ +unsignedBigInteger('point_discount_amount')->default(0)->comment('积分抵扣金额'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('orders', function (Blueprint $table) { + $table->dropColumn(['point_discount_amount']); + }); + } +} diff --git a/database/migrations/2023_10_17_153347_add_point_discount_amount_to_order_products_table.php b/database/migrations/2023_10_17_153347_add_point_discount_amount_to_order_products_table.php new file mode 100644 index 00000000..3c576b13 --- /dev/null +++ b/database/migrations/2023_10_17_153347_add_point_discount_amount_to_order_products_table.php @@ -0,0 +1,32 @@ +unsignedBigInteger('point_discount_amount')->default(0)->comment('积分抵扣金额'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('order_products', function (Blueprint $table) { + $table->dropColumn(['point_discount_amount']); + }); + } +} diff --git a/database/migrations/2023_10_17_165437_add_points_to_after_sales_table.php b/database/migrations/2023_10_17_165437_add_points_to_after_sales_table.php new file mode 100644 index 00000000..8f506d08 --- /dev/null +++ b/database/migrations/2023_10_17_165437_add_points_to_after_sales_table.php @@ -0,0 +1,32 @@ +unsignedBigInteger('points')->default(0)->comment('积分'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('after_sales', function (Blueprint $table) { + $table->dropColumn(['points']); + }); + } +} diff --git a/resources/lang/zh_CN/after-sale.php b/resources/lang/zh_CN/after-sale.php index 2db2c443..be934ddf 100644 --- a/resources/lang/zh_CN/after-sale.php +++ b/resources/lang/zh_CN/after-sale.php @@ -42,6 +42,7 @@ return [ 'tracking_number' => '运单号', 'created_at'=>'申请时间', 'tags' => '标签', + 'points' => '积分', ], 'options' => [ ],