diff --git a/app/Admin/Actions/Grid/PartCoupon.php b/app/Admin/Actions/Grid/PartCoupon.php new file mode 100644 index 00000000..0b322e7d --- /dev/null +++ b/app/Admin/Actions/Grid/PartCoupon.php @@ -0,0 +1,44 @@ +'; + + public function title() + { + if ($this->title) { + return $this->title.' 赠券'; + } + + return '赠券'; + } + + /** + * @param Model|Authenticatable|HasPermissions|null $user + * + * @return bool + */ + protected function authorize($user): bool + { + return $user->can('dcat.admin.product_parts.coupons'); + } + + public function render() + { + $form = PartCouponForm::make()->payload(['id'=>$this->getKey()]); + return Modal::make() + ->lg() + ->title($this->title()) + ->body($form) + ->button($this->title()); + } +} diff --git a/app/Admin/Actions/Show/AfterSaleFinanceShipping.php b/app/Admin/Actions/Show/AfterSaleFinanceShipping.php index a2e38b35..b2d35688 100644 --- a/app/Admin/Actions/Show/AfterSaleFinanceShipping.php +++ b/app/Admin/Actions/Show/AfterSaleFinanceShipping.php @@ -52,7 +52,7 @@ class AfterSaleFinanceShipping extends AbstractTool try { DB::beginTransaction(); $afterSale = AfterSale::where('state', AfterSale::STATE_FINANCE)->findOrFail($key); - $afterSaleService->finance($afterSale); + $afterSaleService->finance($afterSale, '同意换货'); DB::commit(); } catch (Throwable $th) { DB::rollBack(); diff --git a/app/Admin/Controllers/ProductPartController.php b/app/Admin/Controllers/ProductPartController.php index 8da7c262..000d74fc 100644 --- a/app/Admin/Controllers/ProductPartController.php +++ b/app/Admin/Controllers/ProductPartController.php @@ -2,6 +2,7 @@ namespace App\Admin\Controllers; +use App\Admin\Actions\Grid\PartCoupon; use App\Admin\Renderable\ProductPartSkuTable; use App\Admin\Renderable\ProductSkuSimpleTable; use App\Admin\Repositories\ProductPart; @@ -60,6 +61,9 @@ class ProductPartController extends AdminController //删除以及自定义操作 $grid->actions(function (Grid\Displayers\Actions $actions) { $actions->disableDelete(Admin::user()->cannot('dcat.admin.product_parts.destroy')); + if (Admin::user()->can('dcat.admin.product_parts.coupons')) { + $actions->append(new PartCoupon()); + } }); /** 查询 **/ @@ -128,6 +132,7 @@ class ProductPartController extends AdminController $form->switch('is_show')->default(0); + $form->textarea('description'); $form->display('created_at'); $form->display('updated_at'); diff --git a/app/Admin/Forms/PartCoupon.php b/app/Admin/Forms/PartCoupon.php new file mode 100644 index 00000000..102941df --- /dev/null +++ b/app/Admin/Forms/PartCoupon.php @@ -0,0 +1,97 @@ +can('dcat.admin.product_parts.coupons'); + } + + /** + * Handle the form request. + * + * @param array $input + * + * @return mixed + */ + public function handle(array $input) + { + $couponIds = []; + $coupons = []; + $delCouponIds = []; + foreach ($input['coupons'] as $coupon) { + if ($coupon['_remove_'] == 1) { + $delCouponIds[] = $coupon['id']; + } else { + if (is_null($coupon['id'])) { + $coupons[] = new ProductPartCoupon($coupon); + } else { + $_coupon = ProductPartCoupon::find($coupon['id']); + $_coupon->coupon_id = $coupon['coupon_id']; + $_coupon->num = $coupon['num']; + + $coupons[] = $_coupon; + } + } + } + $partId = $input['part_id']; + $part = ProductPart::findOrFail($partId); + + try { + DB::beginTransaction(); + $part->coupons()->saveMany($coupons); + + ProductPartCoupon::whereIn('id', $delCouponIds)->delete(); + DB::commit(); + } catch (Throwable $th) { + DB::rollBack(); + report($th); + return $this->response()->error('操作失败:'.$th->getMessage())->refresh(); + } + + return $this->response() + ->success(__('admin.update_succeeded')) + ->refresh(); + } + + /** + * Build a form here. + */ + public function form() + { + $id = $this->payload['id'] ?? 0; + $part = ProductPart::with('coupons')->findOrFail($id); + + $this->hasMany('coupons', function (NestedForm $form) { + $form->select('coupon_id')->options(function ($id) { + $coupon = Coupon::find($id); + if ($coupon) { + return [$coupon->id => $coupon->name]; + } + })->ajax(admin_route('api.coupons'))->required(); + $form->number('num')->min(1)->default(1); + })->customFormat(function () use ($part) { + return $part->coupons; + }); + $this->hidden('part_id')->value($id); + } +} diff --git a/app/Console/Commands/AdminSendCoupon.php b/app/Console/Commands/AdminSendCoupon.php index 4e48eedd..22ce547d 100644 --- a/app/Console/Commands/AdminSendCoupon.php +++ b/app/Console/Commands/AdminSendCoupon.php @@ -6,6 +6,7 @@ use App\Models\Coupon; use App\Models\CouponSendTask; use App\Models\CouponTaskLog; use App\Models\UserCoupon; +use App\Services\CouponService; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; use Throwable; @@ -53,7 +54,6 @@ class AdminSendCoupon extends Command $logs = CouponTaskLog::where('status', '0')->where('num', '>', 0)->orderBy('id')->limit($this->limit)->get(); $logs = $logs->groupBy('task_id'); foreach ($logs as $taskId => $taskLogs) { - $nowTime = now(); $task = CouponSendTask::find($taskId); $coupon = Coupon::find($task->coupon_id); //如果优惠券限量,则获取优惠券余量,只发送前面的人; @@ -62,30 +62,12 @@ class AdminSendCoupon extends Command $failedLogs = $taskLogs->slice($coupon->stock); $taskLogs = $taskLogs->slice(0, $coupon->stock); } - if ($coupon->use_start_at && $coupon->use_end_at) { - $useStartAt = $coupon->use_start_at; - $useEndAt = $coupon->use_end_at; - } else { - $useStartAt = now(); - $useEndAt = now()->addDays($coupon->use_day); - } //批量插入用户优惠券 $insertUserCoupons = []; foreach ($taskLogs as $taskLog) { for ($i=0; $i< $taskLog->num; $i++) { - $insertUserCoupons[] = [ - 'user_id' => $taskLog->user_id, - 'coupon_id' => $taskLog->coupon_id, - 'coupon_name' => $coupon->name, - 'coupon_type' => $coupon->type, - 'coupon_amount' => (int) bcmul($coupon->amount, 100), - 'coupon_threshold'=> (int) bcmul($coupon->threshold, 100), - 'use_start_at' => $useStartAt, - 'use_end_at' => $useEndAt, - 'created_at' => $nowTime, - 'updated_at' => $nowTime, - ]; + $insertUserCoupons[] = CouponService::createUserCouponData($taskLog->user_id, $coupon); } } try { diff --git a/app/Models/ProductPart.php b/app/Models/ProductPart.php index cf9854e5..d73151e9 100644 --- a/app/Models/ProductPart.php +++ b/app/Models/ProductPart.php @@ -52,4 +52,12 @@ class ProductPart extends Model { return $this->belongsToMany(ProductSku::class, ProductPartSku::class, 'part_id', 'sku_id')->withTimestamps(); } + + /** + * 此分区购买赠送的券 + */ + public function coupons() + { + return $this->hasMany(ProductPartCoupon::class, 'part_id'); + } } diff --git a/app/Models/ProductPartCoupon.php b/app/Models/ProductPartCoupon.php new file mode 100644 index 00000000..917f870b --- /dev/null +++ b/app/Models/ProductPartCoupon.php @@ -0,0 +1,15 @@ +is_show) { + return; + } + //如果用户已领取过该分区设置的券,直接退出; + if (ReceivePartCouponLog::where(['user_id'=>$user->id, 'part_id'=>$part->id])->exists()) { + return; + } + + //取出需要发放的券 + $coupons = Coupon::whereIn('id', $part->coupons->pluck('coupon_id')->toArray())->get(); + $coupons = $coupons->keyBy('id'); + $someCoupons = []; + foreach ($part->coupons as $coupon) { + $someCoupons[] = [ + 'coupon'=>$coupons[$coupon->coupon_id], + 'num'=>$coupon->num, + ]; + } + $this->receiveSomeCoupons($user, $someCoupons); + + ReceivePartCouponLog::create(['user_id'=>$user->id, 'part_id'=>$part->id]); + } + + /** + * 领取一批券 + * + * @param User $user + * @param array $coupons + * @return void + */ + protected function receiveSomeCoupons(User $user, array $coupons) + { + $userCoupons = []; + foreach ($coupons as $coupon) { + for ($i=0; $i<$coupon['num']; $i++) { + $userCoupons[] = self::createUserCouponData($user->id, $coupon['coupon']); + } + } + UserCoupon::insert($userCoupons); + } + + /** + * todo-领取指定券 + * + * @param User $user + * @param Coupon $coupon + * @param int $num + * @return void + */ + public function receiveCoupon(User $user, Coupon $coupon, int $num = 1) + { + return; + } + + /** + * 创建用户券数据 + * + * @return array + */ + public static function createUserCouponData(int $userId, Coupon $coupon) + { + //如果userId小于等于0,直接退出 + if ($userId <= 0) { + return []; + } + if ($coupon->use_start_at && $coupon->use_end_at) { + $useStartAt = $coupon->use_start_at; + $useEndAt = $coupon->use_end_at; + } else { + $useStartAt = now(); + $useEndAt = now()->addDays($coupon->use_day); + } + return [ + 'user_id' => $userId, + 'coupon_id' => $coupon->id, + 'coupon_name' => $coupon->name, + 'coupon_type' => $coupon->type, + 'coupon_amount' => (int) bcmul($coupon->amount, 100), + 'coupon_threshold'=> (int) bcmul($coupon->threshold, 100), + 'use_start_at' => $useStartAt, + 'use_end_at' => $useEndAt, + 'created_at' => now(), + 'updated_at' => now(), + ]; + } } diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 1f3522f7..198f5f4e 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -742,6 +742,9 @@ class OrderService 'status' => Order::STATUS_PAID, ]); + // todo 处理购买分区商品送券 + $this->sendPartCoupon($order); + // todo 处理预收益 return $order; @@ -826,4 +829,31 @@ class OrderService 'status' => Order::STATUS_CANCELLED, ]); } + + /** + * 发送分区优惠券 + * + * @param Order $order + * @return void + */ + protected function sendPartCoupon(Order $order) + { + $products = $order->products()->with('sku.parts')->get(); + // 整理订单商品的分区 + $inValidParts = []; + foreach ($products->pluck('sku.parts') as $parts) { + foreach ($parts as $part) { + if ($part->is_show) { + // 分区去重 + $inValidParts[$part->id] = $part; + } + } + } + + // 将有效的分区丢进去领券 + $couponService = new CouponService(); + foreach ($inValidParts as $inValidPart) { + $couponService->receivePartCoupon($inValidPart, $order->user); + } + } } diff --git a/database/migrations/2021_12_23_094908_create_product_part_coupons_table.php b/database/migrations/2021_12_23_094908_create_product_part_coupons_table.php new file mode 100644 index 00000000..cbda253e --- /dev/null +++ b/database/migrations/2021_12_23_094908_create_product_part_coupons_table.php @@ -0,0 +1,34 @@ +id(); + $table->unsignedBigInteger('part_id')->comment('分区ID'); + $table->unsignedBigInteger('coupon_id')->comment('券ID'); + $table->unsignedInteger('num')->default(0)->comment('张数'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('product_part_coupons'); + } +} diff --git a/database/migrations/2021_12_23_095348_create_receive_part_coupon_logs_table.php b/database/migrations/2021_12_23_095348_create_receive_part_coupon_logs_table.php new file mode 100644 index 00000000..28244b0b --- /dev/null +++ b/database/migrations/2021_12_23_095348_create_receive_part_coupon_logs_table.php @@ -0,0 +1,33 @@ +id(); + $table->unsignedBigInteger('user_id')->comment('用户ID'); + $table->unsignedBigInteger('part_id')->comment('分区ID'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('receive_part_coupon_logs'); + } +} diff --git a/database/migrations/2021_12_23_104444_add_description_to_product_parts_table.php b/database/migrations/2021_12_23_104444_add_description_to_product_parts_table.php new file mode 100644 index 00000000..26b70e62 --- /dev/null +++ b/database/migrations/2021_12_23_104444_add_description_to_product_parts_table.php @@ -0,0 +1,34 @@ +text('description')->nullable()->comment('分区描述'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('product_parts', function (Blueprint $table) { + // + $table->dropColumn('description'); + }); + } +} diff --git a/resources/lang/zh_CN/product-part.php b/resources/lang/zh_CN/product-part.php index 50bd234b..b22a024f 100644 --- a/resources/lang/zh_CN/product-part.php +++ b/resources/lang/zh_CN/product-part.php @@ -13,6 +13,10 @@ return [ 'skus'=> '分区商品', 'sku_id' => '商品名称', 'sort' => '排序', + 'coupons'=>'赠券配置', + 'coupon_id' => '优惠券', + 'num'=>'数量', + 'description'=>'分区描述', ], 'options' => [ ],