Browse Source

添加会员功能

llp 1 month ago
parent
commit
45483b23a1

+ 9 - 3
packages/Longyi/Gift/src/Repositories/CustomInvoiceRepository.php

@@ -16,6 +16,8 @@ class CustomInvoiceRepository extends BaseInvoiceRepository
         $invoice->discount_amount = $invoice->base_discount_amount = 0;
         $invoice->giftcard_amount = $invoice->giftcard_amount = 0;
         $invoice->base_giftcard_amount = $invoice->base_giftcard_amount = 0;
+        $invoice->vip_plus_amount = $invoice->base_vip_plus_amount = 0;
+        $invoice->vip_discount_amount = $invoice->base_vip_discount_amount = 0;
 
         foreach ($invoice->items as $item) {
             $invoice->tax_amount += $item->tax_amount;
@@ -44,6 +46,11 @@ class CustomInvoiceRepository extends BaseInvoiceRepository
         $invoice->base_giftcard_amount += $invoice->order->base_giftcard_amount;
         $invoice->giftcard_number = $invoice->order->giftcard_number;
 
+        $invoice->vip_plus_amount += $invoice->order->vip_plus_amount;
+        $invoice->base_vip_plus_amount += $invoice->order->base_vip_plus_amount;
+        $invoice->vip_discount_amount += $invoice->order->vip_discount_amount;
+        $invoice->base_vip_discount_amount += $invoice->order->base_vip_discount_amount;
+
         if ($invoice->order->shipping_tax_amount) {
             $invoice->shipping_tax_amount = $invoice->order->shipping_tax_amount;
 
@@ -78,11 +85,10 @@ class CustomInvoiceRepository extends BaseInvoiceRepository
             }
         }
 
-        $invoice->grand_total = $invoice->sub_total + $invoice->tax_amount + $invoice->shipping_amount - $invoice->discount_amount - $invoice->giftcard_amount;
-        $invoice->base_grand_total = $invoice->base_sub_total + $invoice->base_tax_amount + $invoice->base_shipping_amount - $invoice->base_discount_amount - $invoice->base_giftcard_amount;
+        $invoice->grand_total = $invoice->sub_total + $invoice->tax_amount + $invoice->shipping_amount - $invoice->discount_amount - $invoice->giftcard_amount + $invoice->vip_plus_amount - $invoice->vip_discount_amount;
+        $invoice->base_grand_total = $invoice->base_sub_total + $invoice->base_tax_amount + $invoice->base_shipping_amount - $invoice->base_discount_amount - $invoice->base_giftcard_amount + $invoice->base_vip_plus_amount - $invoice->base_vip_discount_amount;
 
         $invoice->save();
-//        Event::dispatch('order.invoice.collect.totals.after', $invoice);
         return $invoice;
     }
 }

+ 10 - 2
packages/Longyi/Gift/src/Repositories/CustomOrderRepository.php

@@ -14,6 +14,8 @@ class CustomOrderRepository extends BaseOrderRepository
         $order->tax_amount_invoiced = $order->base_tax_amount_invoiced = 0;
         $order->discount_invoiced = $order->base_discount_invoiced = 0;
         $order->giftcard_amount = $order->base_giftcard_amount = 0;
+        $order->vip_plus_amount = $order->base_vip_plus_amount = 0;
+        $order->vip_discount_amount = $order->base_vip_discount_amount = 0;
 
         foreach ($order->invoices as $invoice) {
             $order->sub_total_invoiced += $invoice->sub_total;
@@ -30,10 +32,16 @@ class CustomOrderRepository extends BaseOrderRepository
 
             $order->giftcard_amount += $invoice->giftcard_amount;
             $order->base_giftcard_amount += $invoice->base_giftcard_amount;
+
+            $order->vip_plus_amount += $invoice->vip_plus_amount;
+            $order->base_vip_plus_amount += $invoice->base_vip_plus_amount;
+
+            $order->vip_discount_amount += $invoice->vip_discount_amount;
+            $order->base_vip_discount_amount += $invoice->base_vip_discount_amount;
         }
 
-        $order->grand_total_invoiced = $order->sub_total_invoiced + $order->shipping_invoiced + $order->tax_amount_invoiced - $order->discount_invoiced - $order->giftcard_amount;
-        $order->base_grand_total_invoiced = $order->base_sub_total_invoiced + $order->base_shipping_invoiced + $order->base_tax_amount_invoiced - $order->base_discount_invoiced - $order->base_giftcard_amount;
+        $order->grand_total_invoiced = $order->sub_total_invoiced + $order->shipping_invoiced + $order->tax_amount_invoiced - $order->discount_invoiced - $order->giftcard_amount + $order->vip_plus_amount - $order->vip_discount_amount;
+        $order->base_grand_total_invoiced = $order->base_sub_total_invoiced + $order->base_shipping_invoiced + $order->base_tax_amount_invoiced - $order->base_discount_invoiced - $order->base_giftcard_amount + $order->base_vip_plus_amount - $order->base_vip_discount_amount;
 
         // order refund total
         $order->sub_total_refunded = $order->base_sub_total_refunded = 0;

+ 2 - 2
packages/Longyi/Member/src/Config/acl.php

@@ -5,6 +5,6 @@ return [
         'key'   => 'member',
         'name'  => 'Member',
         'route' => 'admin.member.index',
-        'sort'  => 2
+        'sort'  => 101
     ]
-];
+];

+ 2 - 2
packages/Longyi/Member/src/Config/admin-menu.php

@@ -5,7 +5,7 @@ return [
         'key'   => 'member',
         'name'  => 'Member',
         'route' => 'admin.member.index',
-        'sort'  => 2,
+        'sort'  => 101,
         'icon'  => 'icon-sales',
     ]
-];
+];

+ 1 - 1
packages/Longyi/Member/src/Contracts/Member.php

@@ -2,6 +2,6 @@
 
 namespace Longyi\Member\Contracts;
 
-interface Member
+interface MemberLog
 {
 }

+ 107 - 0
packages/Longyi/Member/src/DataGrids/Member/MemberDataGrid.php

@@ -0,0 +1,107 @@
+<?php
+
+namespace Longyi\Member\DataGrids\Member;
+
+use Illuminate\Support\Facades\DB;
+use Webkul\DataGrid\DataGrid;
+
+class MemberDataGrid extends DataGrid
+{
+    /**
+     * Prepare query builder.
+     *
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function prepareQueryBuilder()
+    {
+        $queryBuilder = DB::table('member_log')
+            ->leftJoin('customers', 'member_log.customer_id', '=', 'customers.id')
+            ->select(
+                'member_log.id',
+                'member_log.customer_id',
+                'member_log.order_id',
+                'member_log.amount',
+                'customers.email as customer_email',
+                'customers.first_name',
+                'customers.last_name',
+                'member_log.expirationdate',
+                'member_log.created_at',
+                'member_log.updated_at'
+            );
+
+        $this->addFilter('id', 'member_log.id');
+        $this->addFilter('order_id', 'member_log.order_id');
+        $this->addFilter('amount', 'member_log.amount');
+        $this->addFilter('expirationdate', 'member_log.expirationdate');
+        $this->addFilter('created_at', 'member_log.created_at');
+        $this->addFilter('customer_email', 'customers.email');
+
+        return $queryBuilder;
+    }
+
+    /**
+     * Add columns.
+     *
+     * @return void
+     */
+    public function prepareColumns()
+    {
+        $this->addColumn([
+            'index'      => 'id',
+            'label'      => 'id',
+            'type'       => 'integer',
+            'filterable' => true,
+            'sortable'   => true,
+        ]);
+        $this->addColumn([
+            'index'      => 'customer_email',
+            'label'      => '用户邮箱',
+            'type'       => 'string',
+            'filterable' => true,
+            'sortable'   => true,
+            'searchable' => true,
+            'closure'    => function ($row) {
+                if ($row->customer_email) {
+                    $customerName = '';
+                    if ($row->first_name || $row->last_name) {
+                        $customerName = ' (' . trim($row->first_name . ' ' . $row->last_name) . ')';
+                    }
+                    return $row->customer_email . $customerName;
+                }
+            },
+        ]);
+        $this->addColumn([
+            'index'      => 'order_id',
+            'label'      => '#order Id',
+            'type'       => 'integer',
+            'filterable' => false,
+            'sortable'   => true
+        ]);
+        $this->addColumn([
+            'index'      => 'amount',
+            'label'      => 'vip amount',
+            'type'       => 'aggregate',
+            'filterable' => false,
+            'sortable'   => true,
+            'closure'    => function ($row) {
+                return core()->formatPrice($row->amount);
+            },
+        ]);
+        $this->addColumn([
+            'index'      => 'expirationdate',
+            'label'      => 'expirationdate',
+            'type'       => 'date',
+            'filterable' => true,
+            'sortable'   => true,
+        ]);
+
+
+        $this->addColumn([
+            'index'      => 'created_at',
+            'label'      => 'created at',
+            'type'       => 'datetime',
+            'filterable' => true,
+            'sortable'   => true,
+        ]);
+    }
+}

+ 31 - 0
packages/Longyi/Member/src/Database/Migrations/2026_04_20_180518_create_member_log_table.php

@@ -0,0 +1,31 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('member_log', function (Blueprint $table) {
+            $table->id();
+            $table->integer('customer_id')->index()->default(0)->nullable();
+            $table->integer('order_id')->index()->default(0)->nullable();
+            $table->tinyInteger('type')->default(1)->nullable();
+            $table->dateTime('expirationdate')->nullable()->comment('过期时间');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('member_log');
+    }
+};

+ 3 - 56
packages/Longyi/Member/src/Http/Controllers/Admin/MemberController.php

@@ -4,6 +4,7 @@ namespace Longyi\Member\Http\Controllers\Admin;
 
 use Illuminate\Http\JsonResponse;
 use Illuminate\View\View;
+use Longyi\Member\DataGrids\Member\MemberDataGrid;
 use Webkul\Admin\Http\Controllers\Controller;
 
 class MemberController extends Controller
@@ -11,66 +12,12 @@ class MemberController extends Controller
     /**
      * Display a listing of the resource.
      */
-    public function index(): View
+    public function index(): View|JsonResponse
     {
         if (request()->ajax()) {
-            // return datagrid(MemberControllerDataGrid::class)->process();
+            return datagrid(MemberDataGrid::class)->process();
         }
 
         return view('member::admin.index');
     }
-
-    /**
-     * Show the form for creating a new resource.
-     */
-    public function create(): View
-    {
-        return view('member::admin.create');
-    }
-
-    /**
-     * Store a newly created resource in storage.
-     */
-    public function store(): JsonResponse
-    {
-        // Add your store logic here
-        
-        return new JsonResponse([
-            'message' => 'Resource created successfully.',
-        ]);
-    }
-
-    /**
-     * Show the form for editing the specified resource.
-     */
-    public function edit(int $id): View
-    {
-        // $resource = $this->repository->findOrFail($id);
-
-        return view('member::admin.edit', compact('id'));
-    }
-
-    /**
-     * Update the specified resource in storage.
-     */
-    public function update(int $id): JsonResponse
-    {
-        // Add your update logic here
-        
-        return new JsonResponse([
-            'message' => 'Resource updated successfully.',
-        ]);
-    }
-
-    /**
-     * Remove the specified resource from storage.
-     */
-    public function destroy(int $id): JsonResponse
-    {
-        // Add your destroy logic here
-        
-        return new JsonResponse([
-            'message' => 'Resource deleted successfully.',
-        ]);
-    }
 }

+ 0 - 25
packages/Longyi/Member/src/Listeners/InvoicesViewHandler.php

@@ -1,25 +0,0 @@
-<?php
-
-namespace Longyi\Member\Listeners;
-
-use Webkul\Sales\Models\Order;
-
-class InvoicesViewHandler
-{
-    /**
-     * Handle order view information discount before event.
-     *
-     * @param  array  $data
-     * @return void
-     */
-    public function applyMemberDiscount($invoice)
-    {
-        $invoice->vip_plus_amount += $invoice->order->vip_plus_amount;
-        $invoice->base_vip_plus_amount += $invoice->order->base_vip_plus_amount;
-        $invoice->vip_discount_amount += $invoice->order->vip_discount_amount;
-        $invoice->base_vip_discount_amount += $invoice->order->base_vip_discount_amount;
-        $invoice->grand_total = $invoice->grand_total + $invoice->vip_plus_amount - $invoice->vip_discount_amount;
-        $invoice->base_grand_total = $invoice->base_grand_total + $invoice->base_vip_plus_amount- $invoice->base_vip_discount_amount;
-        $invoice->save();
-    }
-}

+ 9 - 11
packages/Longyi/Member/src/Listeners/OrderPlacedHandler.php

@@ -2,6 +2,7 @@
 
 namespace Longyi\Member\Listeners;
 
+use Longyi\Member\Models\MemberLog;
 use Webkul\Paypal\Payment\SmartButton;
 use Webkul\Sales\Repositories\OrderTransactionRepository;
 use Webkul\Customer\Models\Customer;
@@ -34,32 +35,29 @@ class OrderPlacedHandler
         if (!$order || !$order->customer_id) {
             return;
         }
-
-        // 检查订单是否应用了会员优惠
         if (!$order->vip_plus_amount || $order->vip_plus_amount <= 0) {
             return;
         }
-
-        // 获取客户信息
         $customer = Customer::find($order->customer_id);
-
         if (!$customer) {
             return;
         }
-
-        // 计算新的过期时间
         $currentExpireDate = $customer->vip_expire_date ? Carbon::parse($customer->vip_expire_date) : now();
-
-        // 如果已过期,从当前时间开始计算;如果未过期,从过期时间延续
         if ($currentExpireDate->isPast()) {
             $newExpireDate = now()->addDays(self::VIP_EXTENSION_DAYS);
         } else {
             $newExpireDate = $currentExpireDate->addDays(self::VIP_EXTENSION_DAYS);
         }
-
-        // 更新客户的 VIP 过期时间
         $customer->vip_expire_date = $newExpireDate;
         $customer->save();
+        // 记录
+        MemberLog::create([
+            'customer_id' => $order->customer_id,
+            'order_id' => $order->increment_id,
+            'amount' => self::MEMBER_DISCOUNT_AMOUNT,
+            'type' => 1,
+            'expirationdate' => $newExpireDate
+        ]);
     }
 
 }

+ 9 - 7
packages/Longyi/Member/src/Listeners/VipDiscountHandler.php

@@ -6,7 +6,7 @@ use Carbon\Carbon;
 
 class VipDiscountHandler
 {
-    protected const VIP_DISCOUNT_PERCENTAGE = 0.03; // 3%
+    protected const VIP_DISCOUNT_PERCENTAGE = 0.05; // 5%
 
     /**
      * Apply VIP discount to cart.
@@ -19,11 +19,13 @@ class VipDiscountHandler
         if (!$cart || !auth()->guard('customer')->check()) {
             return;
         }
-
+        $isVip = false;
         $customer = auth()->guard('customer')->user();
-
+        if ($cart->vip_plus_amount > 0 ||  $this->isValidVip($customer)) {
+            $isVip = true;
+        }
         // 检查是否是有效 VIP
-        if (!$this->isValidVip($customer)) {
+        if (!$isVip) {
             // 如果不是有效 VIP,清除可能存在的 VIP 折扣
             if ($cart->vip_discount_amount && $cart->vip_discount_amount > 0) {
                 $this->removeVipDiscount($cart);
@@ -38,7 +40,7 @@ class VipDiscountHandler
             return;
         }
 
-        // 计算 3% 折扣
+        // 计算 5% 折扣
         $discountAmountInCurrentCurrency = round($subTotal * self::VIP_DISCOUNT_PERCENTAGE, 2);
         $baseDiscountAmount = core()->convertToBasePrice($discountAmountInCurrentCurrency);
         // 应用新的 VIP 折扣
@@ -72,8 +74,8 @@ class VipDiscountHandler
         $cart->base_vip_discount_amount = null;
 
         // 3. 恢复购物车总额
-        $cart->grand_total = round($cart->grand_total + $discountAmount, 2);
-        $cart->base_grand_total = round($cart->base_grand_total + $baseDiscountAmount, 2);
+        $cart->grand_total = round($cart->grand_total, 2);
+        $cart->base_grand_total = round($cart->base_grand_total, 2);
 
         $cart->save();
     }

+ 0 - 30
packages/Longyi/Member/src/Models/Member.php

@@ -1,30 +0,0 @@
-<?php
-
-namespace Longyi\Member\Models;
-
-use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Model;
-use Longyi\Member\Contracts\Member as MemberContract;
-
-class Member extends Model implements MemberContract
-{
-    use HasFactory;
-
-    /**
-     * The attributes that are mass assignable.
-     *
-     * @var array
-     */
-    protected $fillable = [
-        // Add your fillable attributes here
-    ];
-
-    /**
-     * The attributes that should be cast.
-     *
-     * @var array
-     */
-    protected $casts = [
-        // Add your attribute casts here
-    ];
-}

+ 39 - 0
packages/Longyi/Member/src/Models/MemberLog.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace Longyi\Member\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Longyi\Member\Contracts\MemberLog as MemberLogContract;
+
+class MemberLog extends Model implements MemberLogContract
+{
+    use HasFactory;
+
+    protected $table = 'member_log';
+    /**
+     * The attributes that are mass assignable.
+     *
+     * @var array
+     */
+    protected $fillable = [
+        'customer_id',
+        'order_id',
+        'type',
+        'amount',
+        'expirationdate'
+    ];
+
+    /**
+     * The attributes that should be cast.
+     *
+     * @var array
+     */
+    protected $casts = [
+        'customer_id' => 'integer',
+        'order_id' => 'integer',
+        'type' => 'integer',
+        'amount' => 'decimal:2',
+        'expirationdate' => 'date',
+    ];
+}

+ 1 - 1
packages/Longyi/Member/src/Models/MemberProxy.php

@@ -4,6 +4,6 @@ namespace Longyi\Member\Models;
 
 use Konekt\Concord\Proxies\ModelProxy;
 
-class MemberProxy extends ModelProxy
+class MemberLogProxy extends ModelProxy
 {
 }

+ 0 - 1
packages/Longyi/Member/src/Providers/EventServiceProvider.php

@@ -18,7 +18,6 @@ class EventServiceProvider extends ServiceProvider
     {
         Event::listen('checkout.cart.collect.totals.after', 'Longyi\Member\Listeners\MemberHandler@applyMemberDiscount');
         Event::listen('checkout.cart.collect.totals.after', 'Longyi\Member\Listeners\VipDiscountHandler@applyVipDiscount');
-        Event::listen('order.invoice.collect.totals.after', 'Longyi\Member\Listeners\InvoicesViewHandler@applyMemberDiscount');
         Event::listen('bagisto.shop.checkout.onepage.summary.coupon.after', function($viewRenderEventManager) {
             $viewRenderEventManager->addTemplate('member::components.vip-member-discount-cartsummary');
         });

+ 16 - 0
packages/Longyi/Member/src/Repositories/MemberLogRepository.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace Longyi\Member\Repositories;
+
+use Webkul\Core\Eloquent\Repository;
+
+class MemberLogRepository extends Repository
+{
+    /**
+     * Specify model class name.
+     */
+    public function model(): string
+    {
+        return 'Longyi\Member\Contracts\MemberLog';
+    }
+}

+ 1 - 1
packages/Longyi/Member/src/Resources/lang/en/app.php

@@ -2,7 +2,7 @@
 
 return [
     'member' => [
-        'discount' => '会员优惠',
+        'discount' => 'Plus Fee',
         'vip_active' => 'VIP 会员权益生效中',
         'vip_discount' => 'VIP amount',
     ],

+ 1 - 1
packages/Longyi/Member/src/Resources/lang/zh_CN/app.php

@@ -2,7 +2,7 @@
 
 return [
     'member' => [
-        'discount' => '会员优惠',
+        'discount' => 'Plus Fee',
         'vip_active' => 'VIP 会员权益生效中',
         'vip_discount' => 'VIP amount',
     ],

+ 13 - 7
packages/Longyi/Member/src/Resources/views/admin/index.blade.php

@@ -1,13 +1,19 @@
 <x-admin::layouts>
-
-    <!-- Title of the page -->
     <x-slot:title>
-        Package Member
+        vip管理
     </x-slot>
 
-    <!-- Page Content -->
-    <div class="page-content">
-        <h1>Package Member</h1>
+    @if(session('success'))
+        <div class="mb-4 px-4 py-3 bg-green-500 text-white rounded-lg">
+            {{ session('success') }}
+        </div>
+    @endif
+
+    <div class="flex justify-between items-center">
+        <p class="text-xl text-gray-800 font-bold">
+            vip管理列表
+        </p>
     </div>
 
-</x-admin::layouts>
+    <x-admin::datagrid src="{{ route('admin.member.index') }}"></x-admin::datagrid>
+</x-admin::layouts>