pull/1/head
			
			
		
		
							parent
							
								
									1e8d83658b
								
							
						
					
					
						commit
						abbd10aada
					
				@ -0,0 +1,18 @@
 | 
				
			||||
root = true
 | 
				
			||||
 | 
				
			||||
[*]
 | 
				
			||||
charset = utf-8
 | 
				
			||||
end_of_line = lf
 | 
				
			||||
indent_size = 4
 | 
				
			||||
indent_style = space
 | 
				
			||||
insert_final_newline = true
 | 
				
			||||
trim_trailing_whitespace = true
 | 
				
			||||
 | 
				
			||||
[*.md]
 | 
				
			||||
trim_trailing_whitespace = false
 | 
				
			||||
 | 
				
			||||
[*.{yml,yaml}]
 | 
				
			||||
indent_size = 2
 | 
				
			||||
 | 
				
			||||
[docker-compose.yml]
 | 
				
			||||
indent_size = 4
 | 
				
			||||
@ -0,0 +1,52 @@
 | 
				
			||||
APP_NAME=moon-shop
 | 
				
			||||
APP_ENV=product
 | 
				
			||||
APP_KEY=
 | 
				
			||||
APP_DEBUG=true
 | 
				
			||||
APP_LOG_LEVEL=debug
 | 
				
			||||
# 请务必修改此项为自己的 URL
 | 
				
			||||
APP_URL=http://shop.shiguopeng.cn
 | 
				
			||||
 | 
				
			||||
# 数据库设置
 | 
				
			||||
DB_CONNECTION=mysql
 | 
				
			||||
DB_HOST=
 | 
				
			||||
DB_PORT=3306
 | 
				
			||||
DB_DATABASE=
 | 
				
			||||
DB_USERNAME=
 | 
				
			||||
DB_PASSWORD=
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
# 缓存, session, 队列驱动设置
 | 
				
			||||
BROADCAST_DRIVER=log
 | 
				
			||||
CACHE_DRIVER=file
 | 
				
			||||
SESSION_DRIVER=file
 | 
				
			||||
QUEUE_DRIVER=database
 | 
				
			||||
 | 
				
			||||
# redis 数据库配置
 | 
				
			||||
REDIS_HOST=
 | 
				
			||||
REDIS_PASSWORD=
 | 
				
			||||
REDIS_PORT=6379
 | 
				
			||||
 | 
				
			||||
# 邮箱设置
 | 
				
			||||
MAIL_DRIVER=smtp
 | 
				
			||||
MAIL_HOST=smtp.qq.com
 | 
				
			||||
MAIL_PORT=465
 | 
				
			||||
MAIL_USERNAME=
 | 
				
			||||
MAIL_PASSWORD=
 | 
				
			||||
MAIL_FROM_ADDRESS=
 | 
				
			||||
MAIL_FROM_NAME=MondayShop
 | 
				
			||||
MAIL_ENCRYPTION=ssl
 | 
				
			||||
 | 
				
			||||
# 第三方互联登录
 | 
				
			||||
OAUTH_GITHUB_ID=
 | 
				
			||||
OAUTH_GITHUB_SECRET=
 | 
				
			||||
OAUTH_QQ_ID=
 | 
				
			||||
OAUTH_WEIBO_ID=
 | 
				
			||||
 | 
				
			||||
# 支付宝支付
 | 
				
			||||
ALIAPY_APP_ID=
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
JWT_SECRET=
 | 
				
			||||
 | 
				
			||||
# Docker 运行必须是 0.0.0.0
 | 
				
			||||
LARAVELS_LISTEN_IP=0.0.0.0
 | 
				
			||||
@ -0,0 +1,11 @@
 | 
				
			||||
* text=auto eol=lf
 | 
				
			||||
 | 
				
			||||
*.blade.php diff=html
 | 
				
			||||
*.css diff=css
 | 
				
			||||
*.html diff=html
 | 
				
			||||
*.md diff=markdown
 | 
				
			||||
*.php diff=php
 | 
				
			||||
 | 
				
			||||
/.github export-ignore
 | 
				
			||||
CHANGELOG.md export-ignore
 | 
				
			||||
.styleci.yml export-ignore
 | 
				
			||||
@ -0,0 +1,20 @@
 | 
				
			||||
/.phpunit.cache
 | 
				
			||||
/node_modules
 | 
				
			||||
/public/build
 | 
				
			||||
/public/hot
 | 
				
			||||
/public/storage
 | 
				
			||||
/storage/*.key
 | 
				
			||||
/vendor
 | 
				
			||||
.env
 | 
				
			||||
.env.backup
 | 
				
			||||
.env.production
 | 
				
			||||
.phpunit.result.cache
 | 
				
			||||
Homestead.json
 | 
				
			||||
Homestead.yaml
 | 
				
			||||
auth.json
 | 
				
			||||
npm-debug.log
 | 
				
			||||
yarn-error.log
 | 
				
			||||
/.fleet
 | 
				
			||||
/.idea
 | 
				
			||||
/.vscode
 | 
				
			||||
rr
 | 
				
			||||
@ -0,0 +1,21 @@
 | 
				
			||||
# Security Policy
 | 
				
			||||
 | 
				
			||||
## Supported Versions
 | 
				
			||||
 | 
				
			||||
Use this section to tell people about which versions of your project are
 | 
				
			||||
currently being supported with security updates.
 | 
				
			||||
 | 
				
			||||
| Version | Supported          |
 | 
				
			||||
| ------- | ------------------ |
 | 
				
			||||
| 5.1.x   | :white_check_mark: |
 | 
				
			||||
| 5.0.x   | :x:                |
 | 
				
			||||
| 4.0.x   | :white_check_mark: |
 | 
				
			||||
| < 4.0   | :x:                |
 | 
				
			||||
 | 
				
			||||
## Reporting a Vulnerability
 | 
				
			||||
 | 
				
			||||
Use this section to tell people how to report a vulnerability.
 | 
				
			||||
 | 
				
			||||
Tell them where to go, how often they can expect to get an update on a
 | 
				
			||||
reported vulnerability, what to expect if the vulnerability is accepted or
 | 
				
			||||
declined, etc.
 | 
				
			||||
@ -0,0 +1,22 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Actions\Post;
 | 
				
			||||
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Encore\Admin\Actions\RowAction;
 | 
				
			||||
use Illuminate\Database\Eloquent\Model;
 | 
				
			||||
 | 
				
			||||
class ActiveUserAction extends RowAction
 | 
				
			||||
{
 | 
				
			||||
    public $name = '激活';
 | 
				
			||||
 | 
				
			||||
    public function handle(User $model)
 | 
				
			||||
    {
 | 
				
			||||
        // $model ...
 | 
				
			||||
        $model->is_active = 1;
 | 
				
			||||
        $model->save();
 | 
				
			||||
 | 
				
			||||
        return $this->response()->success('操作成功.')->refresh();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,28 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Actions\Post;
 | 
				
			||||
 | 
				
			||||
use Encore\Admin\Actions\RowAction;
 | 
				
			||||
use Illuminate\Database\Eloquent\Model;
 | 
				
			||||
 | 
				
			||||
class DividerAction extends RowAction
 | 
				
			||||
{
 | 
				
			||||
    public $name = '分隔线';
 | 
				
			||||
 | 
				
			||||
    public function handle(Model $model)
 | 
				
			||||
    {
 | 
				
			||||
        // $model ...
 | 
				
			||||
 | 
				
			||||
        return $this->response()->success('Success message.')->refresh();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Render row action.
 | 
				
			||||
     *
 | 
				
			||||
     * @return string
 | 
				
			||||
     */
 | 
				
			||||
    public function render()
 | 
				
			||||
    {
 | 
				
			||||
        return '<li class="divider"></li>';
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,30 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Actions\Post;
 | 
				
			||||
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use Encore\Admin\Actions\RowAction;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class ForceDeleteProductAction extends RowAction
 | 
				
			||||
{
 | 
				
			||||
    public $name = '删除';
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function handle(Product $product)
 | 
				
			||||
    {
 | 
				
			||||
        $product->forceDelete();
 | 
				
			||||
 | 
				
			||||
        return $this->response()->success('操作成功.')->refresh();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function retrieveModel(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        if (!$key = $request->get('_key')) {
 | 
				
			||||
            return false;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return Product::query()->withTrashed()->findOrFail($key);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,34 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Actions\Post;
 | 
				
			||||
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Encore\Admin\Actions\RowAction;
 | 
				
			||||
use Illuminate\Database\Eloquent\Model;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class OrderReceivedAction extends RowAction
 | 
				
			||||
{
 | 
				
			||||
    public $name = '确认收货';
 | 
				
			||||
 | 
				
			||||
    public function handle(Order $order)
 | 
				
			||||
    {
 | 
				
			||||
        if ($order->status != OrderStatusEnum::PAID) {
 | 
				
			||||
 | 
				
			||||
            return back()->withErrors('订单未付款', 'error');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($order->ship_status != OrderShipStatusEnum::DELIVERED) {
 | 
				
			||||
 | 
				
			||||
            return back()->withErrors('订单未发货', 'error');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $order->ship_status = OrderShipStatusEnum::RECEIVED;
 | 
				
			||||
        $order->save();
 | 
				
			||||
 | 
				
			||||
        return $this->response()->success('确认收货成功.')->refresh();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,59 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Actions\Post;
 | 
				
			||||
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Encore\Admin\Actions\RowAction;
 | 
				
			||||
use Illuminate\Database\Eloquent\Model;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Yansongda\Pay\Pay;
 | 
				
			||||
 | 
				
			||||
class OrderRefundAction extends RowAction
 | 
				
			||||
{
 | 
				
			||||
    public $name = '退款';
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function handle(Order $order, Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        // 订单必须申请支付了,才可才可以退款
 | 
				
			||||
        if ($order->status != OrderStatusEnum::APPLY_REFUND) {
 | 
				
			||||
            return $this->response()->error('订单当前状态禁止退款');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $pay = Pay::alipay(config('pay.ali'));
 | 
				
			||||
 | 
				
			||||
        // 退款数据
 | 
				
			||||
        $refundData = [
 | 
				
			||||
            'out_trade_no' => $order->no,
 | 
				
			||||
            'trade_no' => $order->pay_no,
 | 
				
			||||
            'refund_amount' => $order->pay_amount,
 | 
				
			||||
            'refund_reason' => '正常退款',
 | 
				
			||||
        ];
 | 
				
			||||
 | 
				
			||||
        try {
 | 
				
			||||
 | 
				
			||||
            // 将订单状态改为退款
 | 
				
			||||
            $response = $pay->refund($refundData);
 | 
				
			||||
            $order->pay_refund_fee = $response->get('refund_fee');
 | 
				
			||||
            $order->pay_trade_no = $response->get('trade_no');
 | 
				
			||||
            $order->status = OrderStatusEnum::REFUND;
 | 
				
			||||
            $order->save();
 | 
				
			||||
 | 
				
			||||
        } catch (\Exception $e) {
 | 
				
			||||
 | 
				
			||||
            // 调用异常的处理
 | 
				
			||||
            // abort(500, $e->getMessage());
 | 
				
			||||
            return $this->response()->error('服务器异常,请稍后再试');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return $this->response()->success('退款成功.')->refresh();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function dialog()
 | 
				
			||||
    {
 | 
				
			||||
        $this->confirm('退款会直接把钱退回到支付账户,是否继续');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,44 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Actions\Post;
 | 
				
			||||
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Encore\Admin\Actions\RowAction;
 | 
				
			||||
use Illuminate\Database\Eloquent\Model;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class OrderShipAction extends RowAction
 | 
				
			||||
{
 | 
				
			||||
    public $name = '发货';
 | 
				
			||||
 | 
				
			||||
    public function handle(Order $order, Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        if ($order->status != OrderStatusEnum::PAID) {
 | 
				
			||||
 | 
				
			||||
            return $this->response()->error('订单未付款');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $company = $request->input('company');
 | 
				
			||||
        $no = $request->input('no');
 | 
				
			||||
        if (empty($company) || empty($no)) {
 | 
				
			||||
 | 
				
			||||
            return $this->response()->error('必填项不能为空');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $order->ship_status = OrderShipStatusEnum::DELIVERED;
 | 
				
			||||
        $order->express_company = $company;
 | 
				
			||||
        $order->express_no = $no;
 | 
				
			||||
        $order->save();
 | 
				
			||||
 | 
				
			||||
        return $this->response()->success('发货成功.')->refresh();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function form()
 | 
				
			||||
    {
 | 
				
			||||
        $this->text('company', '物流公司')->required();
 | 
				
			||||
        $this->text('no', '物流单号')->required();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,46 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Actions\Post;
 | 
				
			||||
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use Encore\Admin\Actions\RowAction;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class ProductStatusAction extends RowAction
 | 
				
			||||
{
 | 
				
			||||
    public $name = '';
 | 
				
			||||
 | 
				
			||||
    public function setName($name)
 | 
				
			||||
    {
 | 
				
			||||
        $this->name = $name;
 | 
				
			||||
 | 
				
			||||
        return $this;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function handle(Product $product)
 | 
				
			||||
    {
 | 
				
			||||
        // $model ...
 | 
				
			||||
        // 如果商品已经下架
 | 
				
			||||
        if ($product->trashed()) {
 | 
				
			||||
 | 
				
			||||
            // 重新上架
 | 
				
			||||
            $product->restore();
 | 
				
			||||
        } else {
 | 
				
			||||
 | 
				
			||||
            $product->delete();
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return $this->response()->success('操作成功.')->refresh();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function retrieveModel(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        if (!$key = $request->get('_key')) {
 | 
				
			||||
            return false;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return Product::query()->withTrashed()->findOrFail($key);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,118 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use Encore\Admin\Auth\Database\Administrator;
 | 
				
			||||
use Encore\Admin\Auth\Database\OperationLog;
 | 
				
			||||
use Encore\Admin\Controllers\AuthController as BaseAuthController;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Widgets\Box;
 | 
				
			||||
use Encore\Admin\Widgets\InfoBox;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\Validator;
 | 
				
			||||
 | 
				
			||||
class AdminController extends BaseAuthController
 | 
				
			||||
{
 | 
				
			||||
    // 覆盖默认的
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header(trans('admin.administrator'))
 | 
				
			||||
            ->description(trans('admin.list'))
 | 
				
			||||
            ->body($this->adminGrid()->render());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    protected function adminGrid()
 | 
				
			||||
    {
 | 
				
			||||
        $userModel = config('admin.database.users_model');
 | 
				
			||||
 | 
				
			||||
        $grid = new Grid(new $userModel());
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', 'ID')->sortable();
 | 
				
			||||
        $grid->column('username', trans('admin.username'));
 | 
				
			||||
        $grid->column('name', trans('admin.name'));
 | 
				
			||||
        $grid->column('login_ip', '登录ip');
 | 
				
			||||
        $grid->roles(trans('admin.roles'))->pluck('name')->label();
 | 
				
			||||
        $grid->column('created_at', trans('admin.created_at'));
 | 
				
			||||
        $grid->column('updated_at', trans('admin.updated_at'));
 | 
				
			||||
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\DropdownActions $actions) {
 | 
				
			||||
            if ($actions->getKey() == 1) {
 | 
				
			||||
                $actions->disableDelete();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $grid->tools(function (Grid\Tools $tools) {
 | 
				
			||||
            $tools->batch(function (Grid\Tools\BatchActions $actions) {
 | 
				
			||||
                $actions->disableDelete();
 | 
				
			||||
            });
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function indexLogs(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header(trans('admin.operation_log'))
 | 
				
			||||
            ->description(trans('admin.list'))
 | 
				
			||||
            ->body($this->logGrid());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function logGrid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new OperationLog());
 | 
				
			||||
 | 
				
			||||
        $grid->model()->orderBy('id', 'DESC');
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', 'ID')->sortable();
 | 
				
			||||
        $grid->column('user.name', '用户');
 | 
				
			||||
        $grid->column('method', '方法')->display(function ($method) {
 | 
				
			||||
            $color = array_get(OperationLog::$methodColors, $method, 'grey');
 | 
				
			||||
 | 
				
			||||
            return "<span class=\"badge bg-$color\">$method</span>";
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('path', '路径')->label('info');
 | 
				
			||||
        $grid->column('ip', '地址')->label('primary');
 | 
				
			||||
        $grid->column('description', '描述')->limit(20)->modal(function ($model) {
 | 
				
			||||
 | 
				
			||||
            return new Box('详情', $model->description ?: ' ');
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('input', '输入数据')->limit(20)->expand(function ($model) {
 | 
				
			||||
 | 
				
			||||
            $input = json_decode($model->input, true);
 | 
				
			||||
            $input = array_except($input, ['_pjax', '_token', '_method', '_previous_']);
 | 
				
			||||
            $codes = empty($input) ?
 | 
				
			||||
                '<code>{}</code>' :
 | 
				
			||||
                '<pre>'.json_encode($input, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE).'</pre>';
 | 
				
			||||
 | 
				
			||||
            return new Box('详情', $codes);
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->column('created_at', trans('admin.created_at'));
 | 
				
			||||
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\Actions $actions) {
 | 
				
			||||
            $actions->disableEdit();
 | 
				
			||||
            $actions->disableView();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->disableCreateButton();
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
            $userModel = config('admin.database.users_model');
 | 
				
			||||
 | 
				
			||||
            $filter->equal('user_id', 'User')->select($userModel::all()->pluck('name', 'id'));
 | 
				
			||||
            $filter->equal('method')->select(array_combine(OperationLog::$methods, OperationLog::$methods));
 | 
				
			||||
            $filter->like('path');
 | 
				
			||||
            $filter->equal('ip');
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,99 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\ArticleNotification;
 | 
				
			||||
use Encore\Admin\Controllers\AdminController;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class ArticleNotificationController extends AdminController
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * Title for current resource.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $title = '通知文章';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new ArticleNotification);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', __('Id'));
 | 
				
			||||
        $grid->column('title', __('Title'));
 | 
				
			||||
        $grid->column('created_at', __('Created at'));
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->like('id', __('Id'));
 | 
				
			||||
            $filter->like('title', __('Title'));
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\DropdownActions $actions) {
 | 
				
			||||
 | 
				
			||||
            $actions->disableEdit();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a show builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @return Show
 | 
				
			||||
     */
 | 
				
			||||
    protected function detail($id)
 | 
				
			||||
    {
 | 
				
			||||
        $show = new Show(ArticleNotification::findOrFail($id));
 | 
				
			||||
 | 
				
			||||
        $show->field('id', __('Id'));
 | 
				
			||||
        $show->field('title', __('Title'));
 | 
				
			||||
        $show->field('content', __('Content'))->unescape();
 | 
				
			||||
        $show->field('created_at', __('Created at'));
 | 
				
			||||
 | 
				
			||||
        return $show;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new ArticleNotification);
 | 
				
			||||
 | 
				
			||||
        $form->text('title', __('Title'))->help('请再三确认再发布,不可修改');
 | 
				
			||||
        $form->kindeditor('content', __('Content'));
 | 
				
			||||
    
 | 
				
			||||
        $form->saving(function (Form $form) {
 | 
				
			||||
        
 | 
				
			||||
            if (app()->environment('dev')) {
 | 
				
			||||
            
 | 
				
			||||
                admin_toastr('开发环境不允许操作', 'error');
 | 
				
			||||
                return back()->withInput();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
    
 | 
				
			||||
        $form->deleting(function (Form $form) {
 | 
				
			||||
        
 | 
				
			||||
            if (app()->environment('dev')) {
 | 
				
			||||
            
 | 
				
			||||
                admin_toastr('开发环境不允许操作', 'error');
 | 
				
			||||
                return back()->withInput();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,87 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use Encore\Admin\Auth\Database\Administrator;
 | 
				
			||||
use Encore\Admin\Controllers\AuthController as BaseAuthController;
 | 
				
			||||
use Encore\Admin\Facades\Admin;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\Validator;
 | 
				
			||||
 | 
				
			||||
class AuthController extends BaseAuthController
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 覆盖默认的登录方法
 | 
				
			||||
     *
 | 
				
			||||
     * @param Request $request
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function postLogin(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        $credentials = $request->only([$this->username(), 'password']);
 | 
				
			||||
 | 
				
			||||
        /** @var \Illuminate\Validation\Validator $validator */
 | 
				
			||||
        $validator = Validator::make($credentials, [
 | 
				
			||||
            $this->username()   => 'required',
 | 
				
			||||
            'password'          => 'required',
 | 
				
			||||
        ]);
 | 
				
			||||
 | 
				
			||||
        if ($validator->fails()) {
 | 
				
			||||
            return back()->withInput()->withErrors($validator);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($this->guard()->attempt($credentials)) {
 | 
				
			||||
 | 
				
			||||
            // 验证是否登录的 ip
 | 
				
			||||
            $this->authenticated($this->guard()->user());
 | 
				
			||||
 | 
				
			||||
            // 记录登录日期
 | 
				
			||||
            return $this->sendLoginResponse($request);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return back()->withInput()->withErrors([
 | 
				
			||||
            $this->username() => $this->getFailedLoginMessage(),
 | 
				
			||||
        ]);
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 登录之后提示 ip 地址
 | 
				
			||||
     *
 | 
				
			||||
     * @param Administrator $user
 | 
				
			||||
     */
 | 
				
			||||
    protected function authenticated(Administrator $user)
 | 
				
			||||
    {
 | 
				
			||||
        $ip = request()->getClientIp();
 | 
				
			||||
 | 
				
			||||
        // 如果两次 ip 不一样, 提示风险
 | 
				
			||||
        if (! is_null($user->login_ip) && $ip != $user->login_ip) {
 | 
				
			||||
 | 
				
			||||
            admin_info('上一次登录的地址与本次不同,如果不是本人操作,建议及时修改密码');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $user->login_ip = $ip;
 | 
				
			||||
        $user->save();
 | 
				
			||||
    }
 | 
				
			||||
    
 | 
				
			||||
    public function putSetting()
 | 
				
			||||
    {
 | 
				
			||||
        $form = $this->settingForm();
 | 
				
			||||
        
 | 
				
			||||
        $form->submitted(function (Form $form) {
 | 
				
			||||
    
 | 
				
			||||
            if (app()->environment('dev')) {
 | 
				
			||||
        
 | 
				
			||||
                admin_toastr('开发环境不允许操作', 'error');
 | 
				
			||||
                return back()->withInput();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
        
 | 
				
			||||
        return $form->update(Admin::user()->id);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,90 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\Car;
 | 
				
			||||
use Encore\Admin\Controllers\AdminController;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class CarController extends AdminController
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * Title for current resource.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $title = '购物车数据';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new Car);
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', __('Id'));
 | 
				
			||||
 | 
				
			||||
        $grid->column('user_id', __('User id'));
 | 
				
			||||
        $grid->column('product_id', __('Product id'));
 | 
				
			||||
 | 
				
			||||
        $grid->column('user.name', '用户');
 | 
				
			||||
        $grid->column('product.name', '商品');
 | 
				
			||||
        $grid->column('number', __('Number'));
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $grid->column('created_at', '收藏时间');
 | 
				
			||||
 | 
				
			||||
        $grid->disableActions();
 | 
				
			||||
        $grid->disableCreateButton();
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->equal('user_id', '用户ID');
 | 
				
			||||
            $filter->equal('product_id', '商品ID');
 | 
				
			||||
            $filter->like('user.name', '用户名');
 | 
				
			||||
            $filter->like('product.name', '商品');
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a show builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @return Show
 | 
				
			||||
     */
 | 
				
			||||
    protected function detail($id)
 | 
				
			||||
    {
 | 
				
			||||
        $show = new Show(Car::findOrFail($id));
 | 
				
			||||
 | 
				
			||||
        $show->field('id', __('Id'));
 | 
				
			||||
        $show->field('number', __('Number'));
 | 
				
			||||
        $show->field('product_id', __('Product id'));
 | 
				
			||||
        $show->field('user_id', __('User id'));
 | 
				
			||||
        $show->field('created_at', __('Created at'));
 | 
				
			||||
        $show->field('updated_at', __('Updated at'));
 | 
				
			||||
 | 
				
			||||
        return $show;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new Car);
 | 
				
			||||
 | 
				
			||||
        $form->number('number', __('Number'))->default(1);
 | 
				
			||||
        $form->number('product_id', __('Product id'));
 | 
				
			||||
        $form->number('user_id', __('User id'));
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,197 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\Category;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Layout\Column;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Layout\Row;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
use Encore\Admin\Tree;
 | 
				
			||||
use Encore\Admin\Widgets\Box;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class CategoryController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('商品分类')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->row(function (Row $row) {
 | 
				
			||||
 | 
				
			||||
                // 只能在同一级排序拖动,不允许二级
 | 
				
			||||
                $row->column(6, Category::tree(function (Tree $tree) {
 | 
				
			||||
 | 
				
			||||
                    $tree->disableCreate();
 | 
				
			||||
 | 
				
			||||
                    $tree->nestable(['maxDepth' => 1])
 | 
				
			||||
                         ->branch(function ($branch) {
 | 
				
			||||
 | 
				
			||||
                             $icon = "<i class='fa {$branch['icon']}'></i>";
 | 
				
			||||
 | 
				
			||||
                             return $icon . ' ' . $branch['title'];
 | 
				
			||||
                         });
 | 
				
			||||
                }));
 | 
				
			||||
 | 
				
			||||
                // 新建表单
 | 
				
			||||
                $row->column(6, function (Column $column) {
 | 
				
			||||
                    $form = new \Encore\Admin\Widgets\Form();
 | 
				
			||||
                    $form->action(admin_base_path('categories'));
 | 
				
			||||
 | 
				
			||||
                    $form->text('title', '分类名')->rules('required|unique:categories,title');
 | 
				
			||||
                    $form->icon('icon', '图标')->default('fa-bars')->rules('required');
 | 
				
			||||
                    $form->image('thumb', '缩略图')->uniqueName()->rules('required');
 | 
				
			||||
                    $form->hidden('_token')->default(csrf_token());
 | 
				
			||||
 | 
				
			||||
                    $column->append((new Box('新增', $form))->style('success'));
 | 
				
			||||
                });
 | 
				
			||||
            });
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Show interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function show($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('详情')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->detail($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Edit interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function edit($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('编辑')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->form()->edit($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a show builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @return Show
 | 
				
			||||
     */
 | 
				
			||||
    protected function detail($id)
 | 
				
			||||
    {
 | 
				
			||||
        $show = new Show(Category::findOrFail($id));
 | 
				
			||||
 | 
				
			||||
        $show->field('id');
 | 
				
			||||
        $show->field('title', '分类名');
 | 
				
			||||
        $show->field('thumb', '缩略图')->unescape()->as(function ($thumb) {
 | 
				
			||||
            return image($thumb);
 | 
				
			||||
        });
 | 
				
			||||
        $show->field('description', '描述');
 | 
				
			||||
        $show->field('order', '排序');
 | 
				
			||||
        $show->field('created_at', '创建时间');
 | 
				
			||||
        $show->field('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
        return $show;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new Category);
 | 
				
			||||
 | 
				
			||||
        $form->text('title', '分类名');
 | 
				
			||||
        $form->icon('icon', '图标');
 | 
				
			||||
        $form->image('thumb', '缩略图');
 | 
				
			||||
        $form->text('description', '描述');
 | 
				
			||||
    
 | 
				
			||||
        $form->saving(function (Form $form) {
 | 
				
			||||
        
 | 
				
			||||
            if (app()->environment('dev')) {
 | 
				
			||||
            
 | 
				
			||||
                admin_toastr('开发环境不允许操作', 'error');
 | 
				
			||||
                return back()->withInput();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
    
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 分类下有商品,不允许删除
 | 
				
			||||
     *
 | 
				
			||||
     * @param $id
 | 
				
			||||
     * @return \Illuminate\Http\JsonResponse
 | 
				
			||||
     */
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        if (app()->environment('dev')) {
 | 
				
			||||
        
 | 
				
			||||
            return response()->json(['status' => false, 'message' => '开发环境不允许操作']);
 | 
				
			||||
        }
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $category Category
 | 
				
			||||
         */
 | 
				
			||||
        $category = Category::query()->findOrFail($id);
 | 
				
			||||
 | 
				
			||||
        if ($category->products()->exists()) {
 | 
				
			||||
            return response()->json(['status' => false, 'message' => '分类下有商品存在,不允许删除']);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($category->delete()) {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => true,
 | 
				
			||||
                'message' => trans('admin.delete_succeeded'),
 | 
				
			||||
            ];
 | 
				
			||||
        } else {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => trans('admin.delete_failed'),
 | 
				
			||||
            ];
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return response()->json($data);
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 商品下拉列表
 | 
				
			||||
     *
 | 
				
			||||
     * @param Request $request
 | 
				
			||||
     * @return \Illuminate\Support\Collection
 | 
				
			||||
     */
 | 
				
			||||
    public function getProducts(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        $id = $request->get('q');
 | 
				
			||||
 | 
				
			||||
        $category = Category::query()->findOrFail($id);
 | 
				
			||||
 | 
				
			||||
        // 尽量促销卖得少的商品
 | 
				
			||||
        return $category->products()->orderBy('sale_count', 'desc')->get(['id', 'name as text']);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,73 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\Comment;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
 | 
				
			||||
class CommentController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('评论列表')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->grid());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new Comment);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id');
 | 
				
			||||
        $grid->column('order_id', '订单');
 | 
				
			||||
        $grid->column('product.name', '商品');
 | 
				
			||||
        $grid->column('user.name', '用户');
 | 
				
			||||
        $grid->column('content', '评论内容');
 | 
				
			||||
        $grid->column('score', '评分');
 | 
				
			||||
        $grid->column('created_at', '创建时间');
 | 
				
			||||
        $grid->column('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->disableIdFilter();
 | 
				
			||||
            $filter->where(function ($query) {
 | 
				
			||||
 | 
				
			||||
                $collections = User::query()
 | 
				
			||||
                                   ->where('name', 'like', "%{$this->input}%")
 | 
				
			||||
                                   ->pluck('id');
 | 
				
			||||
                $query->whereIn('user_id', $collections->all());
 | 
				
			||||
            }, '用户');
 | 
				
			||||
            $filter->where(function ($query) {
 | 
				
			||||
 | 
				
			||||
                $collections = Product::query()
 | 
				
			||||
                                      ->where('name', 'like', "%{$this->input}%")
 | 
				
			||||
                                      ->pluck('id');
 | 
				
			||||
 | 
				
			||||
                $query->whereIn('product_id', $collections->all());
 | 
				
			||||
            }, '商品');
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,190 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Enums\UserSexEnum;
 | 
				
			||||
use App\Enums\UserSourceEnum;
 | 
				
			||||
use App\Models\CouponCode;
 | 
				
			||||
use App\Models\CouponTemplate;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Notifications\CouponCodeNotification;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Encore\Admin\Controllers\AdminController;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Notifications\DatabaseNotification;
 | 
				
			||||
use Ramsey\Uuid\Uuid;
 | 
				
			||||
 | 
				
			||||
class CouponCodeController extends AdminController
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * Title for current resource.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $title = '优惠券兑换码';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new CouponCode);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', __('Id'));
 | 
				
			||||
        $grid->column('code', '兑换码');
 | 
				
			||||
        $grid->column('user_id', __('User id'));
 | 
				
			||||
        $grid->column('template_id', '优惠券ID');
 | 
				
			||||
 | 
				
			||||
        $grid->column('user.name', '会员名');
 | 
				
			||||
        $grid->column('template.title', '优惠券名');
 | 
				
			||||
 | 
				
			||||
        $grid->column('used_at', __('Used at'));
 | 
				
			||||
        $grid->column('notification_at', '上一次发送通知时间');
 | 
				
			||||
        $grid->column('created_at', __('Created at'));
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->equal('code', '兑换码');
 | 
				
			||||
            $filter->like('user.name', '会员名');
 | 
				
			||||
            $filter->equal('template.title', '优惠券名');
 | 
				
			||||
            $filter->between('used_at')->datetime();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\Actions $actions) {
 | 
				
			||||
 | 
				
			||||
            $actions->disableEdit();
 | 
				
			||||
            $actions->disableView();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function store()
 | 
				
			||||
    {
 | 
				
			||||
        $request = request();
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * 发放的优惠券不能为空
 | 
				
			||||
         * @var $template CouponTemplate
 | 
				
			||||
         */
 | 
				
			||||
        $template = CouponTemplate::query()->findOrFail($request->input('template_id'));
 | 
				
			||||
        $today = Carbon::today();
 | 
				
			||||
        if ($today->gt(Carbon::make($template->end_date))) {
 | 
				
			||||
 | 
				
			||||
            admin_error('优惠券已过期');
 | 
				
			||||
            return back()->withInput();
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 如果会员 ID 不为空,则代表是指定会员发放
 | 
				
			||||
        if ($userIds = $request->input('user_ids')) {
 | 
				
			||||
 | 
				
			||||
            // 使用空格拆分 id
 | 
				
			||||
            $ids = array_values(array_filter(explode(' ', $userIds)));
 | 
				
			||||
            $users = User::query()->findMany($ids);
 | 
				
			||||
        }
 | 
				
			||||
        // 否则则是条件范围发放
 | 
				
			||||
        else {
 | 
				
			||||
 | 
				
			||||
            $query = User::query();
 | 
				
			||||
            // 性别
 | 
				
			||||
            $sex = array_filter($request->input('user_sex'));
 | 
				
			||||
            // 会员来源
 | 
				
			||||
            $sources = array_filter($request->input('user_source'));
 | 
				
			||||
            // 登录次数
 | 
				
			||||
            $loginCount = (int)$request->input('login_count');
 | 
				
			||||
            // 会员总积分
 | 
				
			||||
            $scoreAll = (int)$request->input('user_score');
 | 
				
			||||
 | 
				
			||||
            $query->whereIn('sex', $sex)
 | 
				
			||||
                  ->whereIn('source', $sources)
 | 
				
			||||
                  ->where('login_count', '>=', $loginCount)
 | 
				
			||||
                  ->where('score_all', '>=', $scoreAll);
 | 
				
			||||
            $users = $query->get();
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 开始根据用户发放
 | 
				
			||||
        $now = Carbon::now()->toDateTimeString();
 | 
				
			||||
        $notifications = collect();
 | 
				
			||||
        $codes = $users->map(function (User $user) use ($template, $notifications, $now) {
 | 
				
			||||
 | 
				
			||||
            $code = strtoupper(str_random(16));
 | 
				
			||||
 | 
				
			||||
            $notification = [
 | 
				
			||||
                'id' => Uuid::uuid4()->toString(),
 | 
				
			||||
                'type' => CouponCodeNotification::class,
 | 
				
			||||
                'notifiable_id' => $user->id,
 | 
				
			||||
                'notifiable_type' => get_class($user),
 | 
				
			||||
                'data' => json_encode((new CouponCodeNotification($template, $code))->toArray($user), JSON_UNESCAPED_UNICODE),
 | 
				
			||||
                'created_at' => $now,
 | 
				
			||||
                'updated_at' => $now,
 | 
				
			||||
            ];
 | 
				
			||||
            $notifications->push($notification);
 | 
				
			||||
 | 
				
			||||
            return [
 | 
				
			||||
                'code' => $code,
 | 
				
			||||
                'user_id' => $user->id,
 | 
				
			||||
                'template_id' => $template->id,
 | 
				
			||||
                'notification_at' => $now,
 | 
				
			||||
                'created_at' => $now,
 | 
				
			||||
                'updated_at' => $now,
 | 
				
			||||
            ];
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $size = 1000;
 | 
				
			||||
        foreach (array_chunk($codes->all(), $size, true) as $chunk) {
 | 
				
			||||
            // 优惠券码
 | 
				
			||||
            CouponCode::query()->insert($codes->all());
 | 
				
			||||
        }
 | 
				
			||||
        foreach (array_chunk($notifications->all(), $size, true) as $chunk) {
 | 
				
			||||
            // 通知
 | 
				
			||||
            DatabaseNotification::query()->insert($chunk);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        admin_toastr("发布成功,总共有{$users->count()}位会员符合发放条件");
 | 
				
			||||
        return response()->redirectTo(admin_url('/coupon_codes'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new CouponCode);
 | 
				
			||||
 | 
				
			||||
        $today = Carbon::today()->toDateString();
 | 
				
			||||
 | 
				
			||||
        $form->divider('指定会员发放');
 | 
				
			||||
        $form->text('user_ids', '会员')->help('请输入会员的ID, 多个会员用空格隔开,如果为空则代表是范围发放');
 | 
				
			||||
 | 
				
			||||
        $form->divider('范围发放');
 | 
				
			||||
        $templates = CouponTemplate::query()->where('end_date', '>=', $today)->pluck('title', 'id');
 | 
				
			||||
        $form->checkbox('user_sex', '会员性别')->options([UserSexEnum::MAN => '男', UserSexEnum::WOMAN => '女'])->canCheckAll();
 | 
				
			||||
        $form->checkbox('user_source', '会员来源')->options([
 | 
				
			||||
                                                            UserSourceEnum::MOON => '前台注册',
 | 
				
			||||
                                                            UserSourceEnum::GITHUB => 'Github',
 | 
				
			||||
                                                            UserSourceEnum::QQ => 'QQ',
 | 
				
			||||
                                                            UserSourceEnum::WEIBO => '微博',
 | 
				
			||||
                                                        ])->canCheckAll();
 | 
				
			||||
        $form->number('login_count', '会员登录次数')->default(0)->help('会员登录次数大于等于给定的次数');
 | 
				
			||||
        $form->number('user_score', '会员总积分')->default(0)->help('会员给定的积分大于等于总积分');
 | 
				
			||||
 | 
				
			||||
        $form->divider('优惠券');
 | 
				
			||||
 | 
				
			||||
        $form->select('template_id', '优惠券')->help('发放的优惠券')->options($templates)->required();
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,57 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Admin\Transforms\YesNoTransform;
 | 
				
			||||
use App\Models\UserHasCoupon;
 | 
				
			||||
use Encore\Admin\Controllers\AdminController;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class CouponLogController extends AdminController
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * Title for current resource.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $title = '会员领取记录';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new UserHasCoupon);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', __('Id'));
 | 
				
			||||
        $grid->column('user_id', '用户 ID');
 | 
				
			||||
        $grid->column('user.name', '用户昵称');
 | 
				
			||||
        $grid->column('title', __('Title'));
 | 
				
			||||
        $grid->column('amount', '满减金额');
 | 
				
			||||
        $grid->column('full_amount', __('Full amount'));
 | 
				
			||||
        $grid->column('start_date', __('Start date'));
 | 
				
			||||
        $grid->column('end_date', __('End date'));
 | 
				
			||||
        $grid->column('is_used', '是否使用')->display(function () {
 | 
				
			||||
 | 
				
			||||
            return YesNoTransform::trans(! is_null($this->used_at));
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('used_at', '使用时间');
 | 
				
			||||
        $grid->column('created_at', __('Created at'));
 | 
				
			||||
 | 
				
			||||
        $grid->disableActions();
 | 
				
			||||
        $grid->disableCreateButton();
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->like('title', __('Title'));
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,86 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Admin\Transforms\YesNoTransform;
 | 
				
			||||
use App\Models\CouponTemplate;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Encore\Admin\Controllers\AdminController;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class CouponTemplateController extends AdminController
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * Title for current resource.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $title = '优惠券';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new CouponTemplate);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest('start_date');
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', __('Id'));
 | 
				
			||||
        $grid->column('title', __('Title'));
 | 
				
			||||
        $grid->column('amount', '优惠金额');
 | 
				
			||||
        $grid->column('full_amount', __('Full amount'));
 | 
				
			||||
        $grid->column('score', '需兑换积分');
 | 
				
			||||
        $grid->column('exp_date', '有效日期')->display(function () {
 | 
				
			||||
 | 
				
			||||
            return $this->start_date . ' ~ ' . $this->end_date;
 | 
				
			||||
        });
 | 
				
			||||
        $today = Carbon::today();
 | 
				
			||||
        $grid->column('overtime', '是否有效(未过期)')->display(function () use ($today) {
 | 
				
			||||
 | 
				
			||||
            $endDate = Carbon::make($this->end_date);
 | 
				
			||||
            $startDate = Carbon::make($this->start_date);
 | 
				
			||||
            // 如果没有结束日期,代表永远不过期
 | 
				
			||||
            if (is_null($endDate) || is_null($startDate)) {
 | 
				
			||||
 | 
				
			||||
                $isOver = true;
 | 
				
			||||
            } else {
 | 
				
			||||
 | 
				
			||||
                $isOver = $today->gte($startDate) && $today->lte($endDate);
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
            return YesNoTransform::trans($isOver);
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('created_at', __('Created at'));
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->like('title', __('Title'));
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new CouponTemplate);
 | 
				
			||||
 | 
				
			||||
        $form->text('title', __('Title'));
 | 
				
			||||
        $form->decimal('amount', '优惠金额');
 | 
				
			||||
        $form->decimal('full_amount', __('Full amount'));
 | 
				
			||||
        $form->number('score', __('Score'))->default(0)->help('设置为 0 代表无需积分即可兑换优惠券');
 | 
				
			||||
        $form->date('start_date', __('Start date'))->default(date('Y-m-d'));
 | 
				
			||||
        $form->date('end_date', __('End date'))->default(Carbon::today()->addMonth());
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,84 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Admin\Extensions\Div;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\SiteCount;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Services\SiteCountService;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Encore\Admin\Auth\Database\Menu;
 | 
				
			||||
use Encore\Admin\Facades\Admin;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Layout\Row;
 | 
				
			||||
use Encore\Admin\Widgets\Box;
 | 
				
			||||
 | 
				
			||||
class HomeController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function index(Content $content, SiteCountService $service)
 | 
				
			||||
    {
 | 
				
			||||
        // 用于更新菜单数据到文件, 可删除
 | 
				
			||||
//        file_put_contents(database_path('data/menus.json'), Menu::all()->toJson(JSON_UNESCAPED_UNICODE));
 | 
				
			||||
 | 
				
			||||
        // 使用 echart
 | 
				
			||||
        Admin::js('/js/echarts.min.js');
 | 
				
			||||
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('仪表盘')
 | 
				
			||||
            ->row(function (Row $row) use ($service) {
 | 
				
			||||
 | 
				
			||||
                /**
 | 
				
			||||
                 * 今日统计,今天的特殊,需要从缓存 redis 中读取
 | 
				
			||||
                 *
 | 
				
			||||
                 * @var $todaySite SiteCount
 | 
				
			||||
                 */
 | 
				
			||||
                $now = Carbon::now();
 | 
				
			||||
                $today = $now->toDateString();
 | 
				
			||||
                $todaySite = SiteCount::query()->firstOrNew(['date' => $today]);
 | 
				
			||||
                $todaySite = $service->syncByCache($todaySite);
 | 
				
			||||
 | 
				
			||||
                // 七日统计
 | 
				
			||||
                $lastWeekDate = $now->subDay(7);
 | 
				
			||||
                $weekSites = SiteCount::query()
 | 
				
			||||
                                      ->where('date', '!=', $today)
 | 
				
			||||
                                      ->where('date', '>', $lastWeekDate)
 | 
				
			||||
                                      ->get()
 | 
				
			||||
                                      ->push($todaySite)
 | 
				
			||||
                                      ->sortBy('date');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                // 本月统计
 | 
				
			||||
                $month = $now->format('Y-m');
 | 
				
			||||
                $monthSites = SiteCount::query()
 | 
				
			||||
                                       ->where('date', '!=', $today)
 | 
				
			||||
                                       ->where('date', '>', $month)
 | 
				
			||||
                                       ->get()
 | 
				
			||||
                                       ->push($todaySite)
 | 
				
			||||
                                       ->sortBy('date');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                $row->column(4, new Box('今日用户注册来源', new Div('todayRegister')));
 | 
				
			||||
                $row->column(4, new Box('七日用户注册来源', new Div('weekRegister')));
 | 
				
			||||
                $row->column(4, new Box('本月用户注册来源', new Div('monthRegister')));
 | 
				
			||||
 | 
				
			||||
                $row->column(4, new Box('今日订单', new Div('todayOrders')));
 | 
				
			||||
                $row->column(4, new Box('近期订单量', new Div('weekSites')));
 | 
				
			||||
                $row->column(4, new Box('交易金额', new Div('saleMoney')));
 | 
				
			||||
 | 
				
			||||
                $allSites = compact('todaySite', 'weekSites', 'monthSites');
 | 
				
			||||
                $row->column(12, view('admin.chars.echart', $allSites));
 | 
				
			||||
            });
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 自定义 404 页面
 | 
				
			||||
     *
 | 
				
			||||
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 | 
				
			||||
     */
 | 
				
			||||
    public function noFound()
 | 
				
			||||
    {
 | 
				
			||||
        return redirect('admin');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,192 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\Level;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class LevelController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('列表')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->grid());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Show interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function show($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('详情')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->detail($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Edit interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function edit($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('Edit')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->form()->edit($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function create(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('新建')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->form());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new Level);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->orderBy('min_score', 'desc');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', 'id');
 | 
				
			||||
        $grid->column('icon', '图标')->image('', 90, 90);
 | 
				
			||||
        $grid->column('name', '名字')->editable();
 | 
				
			||||
        $grid->column('level', '等级');
 | 
				
			||||
        $grid->column('min_score', '分阶');
 | 
				
			||||
        $grid->column('created_at', '创建时间');
 | 
				
			||||
        $grid->column('updated_at', '更新时间');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\Actions $actions) {
 | 
				
			||||
 | 
				
			||||
            $level = $actions->row;
 | 
				
			||||
            if (! $level->can_delete) {
 | 
				
			||||
                $actions->disableDelete();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a show builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @return Show
 | 
				
			||||
     */
 | 
				
			||||
    protected function detail($id)
 | 
				
			||||
    {
 | 
				
			||||
        $show = new Show(Level::findOrFail($id));
 | 
				
			||||
 | 
				
			||||
        $show->field('id', 'id');
 | 
				
			||||
        $show->field('icon', '图标')->image();
 | 
				
			||||
        $show->field('name', '名字');
 | 
				
			||||
        $show->field('level', '等级');
 | 
				
			||||
        $show->field('min_score', '分阶');
 | 
				
			||||
        $show->field('created_at', '创建时间');
 | 
				
			||||
        $show->field('updated_at', '更新时间');
 | 
				
			||||
 | 
				
			||||
        return $show;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new Level);
 | 
				
			||||
 | 
				
			||||
        $icon = $form->image('icon', '图标')->uniqueName();
 | 
				
			||||
        if (! windows_os()) {
 | 
				
			||||
            $icon->resize(160, 160);;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $form->text('name', '名字');
 | 
				
			||||
        $form->number('level', '等级');
 | 
				
			||||
        $form->number('min_score', '积分');
 | 
				
			||||
    
 | 
				
			||||
        $form->saving(function (Form $form) {
 | 
				
			||||
        
 | 
				
			||||
            if (app()->environment('dev')) {
 | 
				
			||||
            
 | 
				
			||||
                admin_toastr('开发环境不允许操作', 'error');
 | 
				
			||||
                return back()->withInput();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
        
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Remove the specified resource from storage.
 | 
				
			||||
     *
 | 
				
			||||
     * @param int $id
 | 
				
			||||
     * @return \Illuminate\Http\Response
 | 
				
			||||
     * @throws \Exception
 | 
				
			||||
     */
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        $level = Level::query()->findOrFail($id);
 | 
				
			||||
 | 
				
			||||
        if (! $level->can_delete) {
 | 
				
			||||
 | 
				
			||||
            return response()->json([
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => '这个等级不允许删除',
 | 
				
			||||
            ]);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($level->delete()) {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => true,
 | 
				
			||||
                'message' => trans('admin.delete_succeeded'),
 | 
				
			||||
            ];
 | 
				
			||||
        } else {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => trans('admin.delete_failed'),
 | 
				
			||||
            ];
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return response()->json($data);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,267 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Admin\Actions\Post\OrderReceivedAction;
 | 
				
			||||
use App\Admin\Actions\Post\OrderRefundAction;
 | 
				
			||||
use App\Admin\Actions\Post\OrderShipAction;
 | 
				
			||||
use App\Admin\Extensions\ReceivedButton;
 | 
				
			||||
use App\Admin\Extensions\ShipButton;
 | 
				
			||||
use App\Admin\Transforms\OrderDetailTransform;
 | 
				
			||||
use App\Admin\Transforms\OrderPayTypeTransform;
 | 
				
			||||
use App\Admin\Transforms\OrderShipStatusTransform;
 | 
				
			||||
use App\Admin\Transforms\OrderStatusTransform;
 | 
				
			||||
use App\Admin\Transforms\OrderTypeTransform;
 | 
				
			||||
use App\Admin\Transforms\YesNoTransform;
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Facades\Admin;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Grid\Displayers\Actions;
 | 
				
			||||
use Encore\Admin\Grid\Filter;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
use Encore\Admin\Widgets\Form;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\DB;
 | 
				
			||||
use Yansongda\Pay\Pay;
 | 
				
			||||
 | 
				
			||||
class OrderController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('订单列表')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->grid());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Show interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function show($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('详情')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->detail($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new Order);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->withTrashed()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id');
 | 
				
			||||
        $grid->column('no', '流水号');
 | 
				
			||||
        $grid->column('user.name', '用户');
 | 
				
			||||
        $grid->column('origin_amount', '订单原价');
 | 
				
			||||
        $grid->column('post_amount', '邮费');
 | 
				
			||||
        $grid->column('coupon_amount', '优惠');
 | 
				
			||||
        $grid->column('amount', '总价');
 | 
				
			||||
        $grid->column('status', '状态')->display(function ($status) {
 | 
				
			||||
 | 
				
			||||
            // 如果订单是付款, 那么就修改为物流状态
 | 
				
			||||
            if ($status == OrderStatusEnum::PAID) {
 | 
				
			||||
 | 
				
			||||
                return OrderShipStatusTransform::trans($this->ship_status);
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
            return OrderStatusTransform::trans($status);
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('type', '订单类型')->display(function ($type) {
 | 
				
			||||
 | 
				
			||||
            return OrderTypeTransform::trans($type);
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->column('pay_type', '支付方式')->display(function ($type) {
 | 
				
			||||
 | 
				
			||||
            return OrderPayTypeTransform::trans($type);
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('pay_no', '支付流水号');
 | 
				
			||||
        $grid->column('paid_at', '支付时间');
 | 
				
			||||
        $grid->column('consignee_name', '收货人姓名');
 | 
				
			||||
        $grid->column('consignee_phone', '收货人手机');
 | 
				
			||||
        $grid->column('created_at', '创建时间');
 | 
				
			||||
 | 
				
			||||
        $grid->disableRowSelector();
 | 
				
			||||
        $grid->disableCreateButton();
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\DropdownActions $actions) {
 | 
				
			||||
 | 
				
			||||
            /**
 | 
				
			||||
             * @var $order Order
 | 
				
			||||
             */
 | 
				
			||||
            $order = $actions->row;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
            // 如果出现了申请,显示可以退款按钮
 | 
				
			||||
            if ($order->status == OrderStatusEnum::APPLY_REFUND) {
 | 
				
			||||
 | 
				
			||||
                $actions->add(new OrderRefundAction());
 | 
				
			||||
            } elseif ($order->status == OrderStatusEnum::PAID) {
 | 
				
			||||
 | 
				
			||||
                if ($order->ship_status == OrderShipStatusEnum::PENDING) {
 | 
				
			||||
 | 
				
			||||
                    $actions->add(new OrderShipAction());
 | 
				
			||||
                } elseif ($order->ship_status == OrderShipStatusEnum::DELIVERED) {
 | 
				
			||||
 | 
				
			||||
                    $actions->add(new OrderReceivedAction());
 | 
				
			||||
                }
 | 
				
			||||
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
            $actions->disableEdit();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->disableIdFilter();
 | 
				
			||||
            $filter->like('no', '流水号');
 | 
				
			||||
            $filter->where(function ($query) {
 | 
				
			||||
 | 
				
			||||
                $users = User::query()->where('name', 'like', "%{$this->input}%")->pluck('id');
 | 
				
			||||
                $query->whereIn('user_id', $users->all());
 | 
				
			||||
            }, '用户');
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a show builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @return Show
 | 
				
			||||
     */
 | 
				
			||||
    protected function detail($id)
 | 
				
			||||
    {
 | 
				
			||||
        $show = new Show(Order::query()->withTrashed()->findOrFail($id));
 | 
				
			||||
 | 
				
			||||
        $show->field('id');
 | 
				
			||||
        $show->field('no', '流水号');
 | 
				
			||||
        $show->field('user', '用户')->as(function ($user) {
 | 
				
			||||
            return optional($user)->name;
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $show->divider();
 | 
				
			||||
 | 
				
			||||
        $show->field('amount', '总计');
 | 
				
			||||
        $show->field('status', '状态')->as(function ($status) {
 | 
				
			||||
 | 
				
			||||
            // 如果订单是付款, 那么就修改为物流状态
 | 
				
			||||
            if ($status == OrderStatusEnum::PAID) {
 | 
				
			||||
 | 
				
			||||
                return OrderShipStatusTransform::trans($this->ship_status);
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
            return OrderStatusTransform::trans($status);
 | 
				
			||||
        });
 | 
				
			||||
        $show->field('type', '订单类型')->as(function ($type) {
 | 
				
			||||
 | 
				
			||||
            return OrderTypeTransform::trans($type);
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $show->divider();
 | 
				
			||||
 | 
				
			||||
        $show->field('express_company', '物流公司');
 | 
				
			||||
        $show->field('express_no', '物流单号');
 | 
				
			||||
 | 
				
			||||
        $show->divider();
 | 
				
			||||
 | 
				
			||||
        $show->field('consignee_name', '收货人');
 | 
				
			||||
        $show->field('consignee_phone', '收货人手机');
 | 
				
			||||
        $show->field('consignee_address', '收货地址');
 | 
				
			||||
 | 
				
			||||
        $show->divider();
 | 
				
			||||
 | 
				
			||||
        $show->field('pay_type', '支付类型')->as(function ($type) {
 | 
				
			||||
 | 
				
			||||
            return OrderPayTypeTransform::trans($type);
 | 
				
			||||
        });
 | 
				
			||||
        $show->field('refund_reason', '退款理由');
 | 
				
			||||
        $show->field('pay_trade_no', '退款单号');
 | 
				
			||||
        $show->field('pay_no', '支付单号');
 | 
				
			||||
        $show->field('paid_at', '支付时间');
 | 
				
			||||
        $show->field('created_at', '创建时间');
 | 
				
			||||
        $show->field('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
        // 详情
 | 
				
			||||
        $show->details('详情', function (Grid $details) {
 | 
				
			||||
 | 
				
			||||
            $details->column('id');
 | 
				
			||||
            $details->column('product.name', '商品名字');
 | 
				
			||||
            $details->column('price', '单价');
 | 
				
			||||
            $details->column('number', '数量');
 | 
				
			||||
            $details->column('is_commented', '是否评论')->display(function ($is) {
 | 
				
			||||
 | 
				
			||||
                return YesNoTransform::trans($is);
 | 
				
			||||
            });
 | 
				
			||||
            $details->column('total', '小计');
 | 
				
			||||
 | 
				
			||||
            $details->disableRowSelector();
 | 
				
			||||
            $details->disableCreateButton();
 | 
				
			||||
            $details->disableFilter();
 | 
				
			||||
            $details->disableActions();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $show;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 后台删除订单就是真的删除了
 | 
				
			||||
     * @param $id
 | 
				
			||||
     * @return \Illuminate\Http\JsonResponse
 | 
				
			||||
     */
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        try {
 | 
				
			||||
 | 
				
			||||
            DB::transaction(function () use ($id) {
 | 
				
			||||
                /**
 | 
				
			||||
                 * @var $order Order
 | 
				
			||||
                 */
 | 
				
			||||
                $order = Order::withTrashed()->findOrFail($id);
 | 
				
			||||
                $order->details()->delete();
 | 
				
			||||
                $order->forceDelete();
 | 
				
			||||
            });
 | 
				
			||||
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => true,
 | 
				
			||||
                'message' => trans('admin.delete_succeeded'),
 | 
				
			||||
            ];
 | 
				
			||||
        } catch (\Throwable $e) {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => trans('admin.delete_failed') . $e->getMessage(),
 | 
				
			||||
            ];
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return response()->json($data);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,249 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Admin\Actions\Post\DividerAction;
 | 
				
			||||
use App\Admin\Actions\Post\ForceDeleteProductAction;
 | 
				
			||||
use App\Admin\Actions\Post\ProductStatusAction;
 | 
				
			||||
use App\Admin\Transforms\YesNoTransform;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\Category;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class ProductController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('商品列表')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->grid());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Show interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function show($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('详情')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->detail($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Edit interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function edit($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('编辑')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->form()->edit($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function create(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('添加商品')
 | 
				
			||||
            ->description('为你的商城添加一个商品')
 | 
				
			||||
            ->body($this->form());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new Product);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->withTrashed()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id');
 | 
				
			||||
        $grid->column('category.title', '商品类别');
 | 
				
			||||
        $grid->column('name', '商品名')->display(function ($name) {
 | 
				
			||||
            return str_limit($name, 30);
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('thumb', '首图')->image('', 50, 50);
 | 
				
			||||
        $grid->column('price', '价格')->display(function ($price) {
 | 
				
			||||
 | 
				
			||||
            return $price . '/' . $this->original_price;
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('view_count', '浏览次数')->sortable();
 | 
				
			||||
        $grid->column('sale_count', '售出数量')->sortable();
 | 
				
			||||
        $grid->column('count', '库存量')->sortable();
 | 
				
			||||
        $grid->column('deleted_at', '是否上架')->display(function ($isAlive) {
 | 
				
			||||
 | 
				
			||||
            return YesNoTransform::trans(is_null($isAlive));
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('created_at', '创建时间');
 | 
				
			||||
        $grid->column('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 查询过滤
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $categories = Category::query()
 | 
				
			||||
                                  ->orderBy('order')
 | 
				
			||||
                                  ->latest()
 | 
				
			||||
                                  ->pluck('title', 'id')
 | 
				
			||||
                                  ->all();
 | 
				
			||||
 | 
				
			||||
            $filter->disableIdFilter();
 | 
				
			||||
            $filter->equal('category_id', '分类')->select($categories);
 | 
				
			||||
            $filter->equal('id', 'ID');
 | 
				
			||||
            $filter->equal('uuid', 'UUID');
 | 
				
			||||
            $filter->like('name', '商品名字');
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 增加一个上架,下架功能
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\DropdownActions $actions) {
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
            $actions->disableDelete();
 | 
				
			||||
            $actions->add(new ForceDeleteProductAction());
 | 
				
			||||
            $actions->add(new DividerAction());
 | 
				
			||||
 | 
				
			||||
            $name = is_null($actions->row->deleted_at) ?
 | 
				
			||||
                "下架":
 | 
				
			||||
                "上架";
 | 
				
			||||
 | 
				
			||||
            $statusAction = new ProductStatusAction();
 | 
				
			||||
            $statusAction->setName($name);
 | 
				
			||||
 | 
				
			||||
            $actions->add($statusAction);
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a show builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @return Show
 | 
				
			||||
     */
 | 
				
			||||
    protected function detail($id)
 | 
				
			||||
    {
 | 
				
			||||
        $show = new Show(Product::query()->withTrashed()->findOrFail($id));
 | 
				
			||||
 | 
				
			||||
        $show->field('id');
 | 
				
			||||
        $show->field('category.title', '商品类别');
 | 
				
			||||
        $show->field('name', '商品名');
 | 
				
			||||
        $show->field('title', '卖点');
 | 
				
			||||
        $show->field('thumb', '缩略图')->image();
 | 
				
			||||
        $show->field('price', '价格')->as(function ($price) {
 | 
				
			||||
            return $price . '/' . $this->original_price;
 | 
				
			||||
        });
 | 
				
			||||
        $show->field('view_count', '浏览次数');
 | 
				
			||||
        $show->field('sale_count', '售出数量');
 | 
				
			||||
        $show->field('count', '库存量');
 | 
				
			||||
        $show->field('deleted_at', '是否上架')->as(function ($isAlive) {
 | 
				
			||||
            return is_null($isAlive) ? '上架' : '下架';
 | 
				
			||||
        });
 | 
				
			||||
        $show->field('created_at', '创建时间');
 | 
				
			||||
        $show->field('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return $show;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new Product);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $form->select('category_id', '类别')->options(Category::selectOrderAll())->rules('required|exists:categories,id');
 | 
				
			||||
        $form->text('name', '商品名字')->rules(function (Form $form) {
 | 
				
			||||
 | 
				
			||||
            $rules = 'required|max:50|unique:products,name';
 | 
				
			||||
            if ($id = $form->model()->id) {
 | 
				
			||||
                $rules .= ',' . $id;
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
            return $rules;
 | 
				
			||||
        });
 | 
				
			||||
        $form->textarea('title', '卖点')->rules('required|max:199');
 | 
				
			||||
        $form->currency('price', '销售价')->symbol('$')->rules('required|numeric');
 | 
				
			||||
        $form->currency('original_price', '原价')->symbol('$')->rules('required|numeric');
 | 
				
			||||
        $form->number('count', '库存量')->rules('required|integer|min:0');
 | 
				
			||||
 | 
				
			||||
        $form->image('thumb', '缩略图')->uniqueName()->move('products/thumb')->rules('required');
 | 
				
			||||
        $form->multipleImage('pictures', '轮播图')->uniqueName()->move('products/lists');
 | 
				
			||||
 | 
				
			||||
        $form->editor('detail.content', '详情')->rules('required');
 | 
				
			||||
    
 | 
				
			||||
        $form->saving(function (Form $form) {
 | 
				
			||||
        
 | 
				
			||||
            if (app()->environment('dev')) {
 | 
				
			||||
            
 | 
				
			||||
                admin_toastr('开发环境不允许操作', 'error');
 | 
				
			||||
                return back()->withInput();
 | 
				
			||||
            }
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        $product = Product::query()->withTrashed()->findOrFail($id);
 | 
				
			||||
    
 | 
				
			||||
        if (app()->environment('dev')) {
 | 
				
			||||
        
 | 
				
			||||
            admin_toastr('开发环境不允许操作', 'error');
 | 
				
			||||
            return back()->withInput();
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($product->forceDelete()) {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => true,
 | 
				
			||||
                'message' => trans('admin.delete_succeeded'),
 | 
				
			||||
            ];
 | 
				
			||||
        } else {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => trans('admin.delete_failed'),
 | 
				
			||||
            ];
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return response()->json($data);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,59 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\ProductHasUser;
 | 
				
			||||
use Encore\Admin\Controllers\AdminController;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class ProductLikeController extends AdminController
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * Title for current resource.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $title = '商品喜好';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new ProductHasUser);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('user_id', __('User id'));
 | 
				
			||||
        $grid->column('product_id', __('Product id'));
 | 
				
			||||
 | 
				
			||||
        $grid->column('user.name', '用户');
 | 
				
			||||
        $grid->column('created_at', '收藏时间');
 | 
				
			||||
        $grid->column('product.name', '商品');
 | 
				
			||||
        $grid->column('product.price', '价格')->display(function ($price) {
 | 
				
			||||
 | 
				
			||||
            return $price . '/' . $this->product['original_price'];
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('product.thumb', '首图')->image('', 50, 50);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $grid->disableActions();
 | 
				
			||||
        $grid->disableCreateButton();
 | 
				
			||||
        $grid->disableBatchActions();
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->disableIdFilter();
 | 
				
			||||
            $filter->like('user_id', '用户ID');
 | 
				
			||||
            $filter->like('product_id', '商品ID');
 | 
				
			||||
            $filter->like('user.name', '用户名');
 | 
				
			||||
            $filter->equal('product.name', '商品');
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,50 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\ScoreLog;
 | 
				
			||||
use Encore\Admin\Controllers\AdminController;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
 | 
				
			||||
class ScoreLogController extends AdminController
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * Title for current resource.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $title = '积分日志';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new ScoreLog);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', __('Id'));
 | 
				
			||||
        $grid->column('user_id', __('User id'));
 | 
				
			||||
        $grid->column('user.name', '用户名');
 | 
				
			||||
        $grid->column('description', __('Description'));
 | 
				
			||||
        $grid->column('score', __('Score'));
 | 
				
			||||
        $grid->column('created_at', __('Created at'));
 | 
				
			||||
        $grid->column('updated_at', __('Updated at'));
 | 
				
			||||
 | 
				
			||||
        $grid->disableActions();
 | 
				
			||||
        $grid->disableCreateButton();
 | 
				
			||||
 | 
				
			||||
        $grid->filter(function (Grid\Filter $filter) {
 | 
				
			||||
 | 
				
			||||
            $filter->like('user.name', '用户名');
 | 
				
			||||
            $filter->equal('score', '积分');
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,227 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Enums\ScoreRuleIndexEnum;
 | 
				
			||||
use App\Models\ScoreRule;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class ScoreRuleController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('列表')
 | 
				
			||||
            ->description(':xxx 是变量模板,建议不要操作')
 | 
				
			||||
            ->body($this->grid());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Show interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function show($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('详情')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->detail($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Edit interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function edit($id, Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('Edit')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->form($id)->edit($id));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function create(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('新建')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->form());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new ScoreRule);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id', 'id');
 | 
				
			||||
        $grid->column('description', '描述');
 | 
				
			||||
        $grid->column('replace_text', '替换文本');
 | 
				
			||||
        $grid->column('score', '积分');
 | 
				
			||||
        $grid->column('times', '次数')->display(function ($times) {
 | 
				
			||||
 | 
				
			||||
            return $times ? $times : '';
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->column('created_at', '创建时间');
 | 
				
			||||
        $grid->column('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\Actions $actions) {
 | 
				
			||||
 | 
				
			||||
            $rule = $actions->row;
 | 
				
			||||
            if (! $rule->can_delete) {
 | 
				
			||||
                $actions->disableDelete();
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a show builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param mixed $id
 | 
				
			||||
     * @return Show
 | 
				
			||||
     */
 | 
				
			||||
    protected function detail($id)
 | 
				
			||||
    {
 | 
				
			||||
        $show = new Show(ScoreRule::findOrFail($id));
 | 
				
			||||
 | 
				
			||||
        $show->field('id', 'id');
 | 
				
			||||
        $show->field('description', '描述');
 | 
				
			||||
        $show->field('replace_text', '替换文本');
 | 
				
			||||
        $show->field('score', '积分');
 | 
				
			||||
        $show->field('times', '次数')->as(function ($times) {
 | 
				
			||||
 | 
				
			||||
            return $times ? $times : '';
 | 
				
			||||
        });
 | 
				
			||||
        $show->field('created_at', '创建时间');
 | 
				
			||||
        $show->field('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
        return $show;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @param null $id
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form($id = null)
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new ScoreRule);
 | 
				
			||||
 | 
				
			||||
        // 只有新增才能改变类型
 | 
				
			||||
        $options = [ScoreRuleIndexEnum::CONTINUE_LOGIN => '连续登录', ScoreRuleIndexEnum::VISITED_PRODUCT => '每日浏览商品'];
 | 
				
			||||
 | 
				
			||||
        if (is_null($id)) {
 | 
				
			||||
 | 
				
			||||
            $form->select('index_code', '类型')->options($options)->rules('required');
 | 
				
			||||
        }
 | 
				
			||||
        $form->number('score', '积分');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        if (! is_null($id)) {
 | 
				
			||||
 | 
				
			||||
            // 只有当时连续登录和修改的才有次数
 | 
				
			||||
            $scoreRule = ScoreRule::query()->findOrFail($id);
 | 
				
			||||
            $form->textarea('replace_text', '替换文本');
 | 
				
			||||
            $form->textarea('description', '描述');
 | 
				
			||||
 | 
				
			||||
            if (array_key_exists($scoreRule->index_code, $options)) {
 | 
				
			||||
 | 
				
			||||
                $form->number('times', '次数');
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
        } else {
 | 
				
			||||
 | 
				
			||||
            $form->number('times', '次数');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $form->saving(function (Form $form) {
 | 
				
			||||
 | 
				
			||||
            // 如果是新建复制模板
 | 
				
			||||
            if (! $form->model()->exists) {
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                $rule = ScoreRule::query()
 | 
				
			||||
                                 ->where('can_delete', 0)
 | 
				
			||||
                                 ->where('index_code', $form->index_code)
 | 
				
			||||
                                 ->firstOrFail();
 | 
				
			||||
 | 
				
			||||
                $form->model()->description = $rule->description;
 | 
				
			||||
                $form->model()->replace_text = $rule->replace_text;
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Remove the specified resource from storage.
 | 
				
			||||
     *
 | 
				
			||||
     * @param int $id
 | 
				
			||||
     * @return \Illuminate\Http\Response
 | 
				
			||||
     * @throws \Exception
 | 
				
			||||
     */
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        $rule = ScoreRule::query()->findOrFail($id);
 | 
				
			||||
 | 
				
			||||
        if (! $rule->can_delete) {
 | 
				
			||||
 | 
				
			||||
            return response()->json([
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => '这个等级不允许删除',
 | 
				
			||||
            ]);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($rule->delete()) {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => true,
 | 
				
			||||
                'message' => trans('admin.delete_succeeded'),
 | 
				
			||||
            ];
 | 
				
			||||
        } else {
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => trans('admin.delete_failed'),
 | 
				
			||||
            ];
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return response()->json($data);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,222 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Http\Requests\StoreSeckillRequest;
 | 
				
			||||
use App\Models\Category;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\Seckill;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\DB;
 | 
				
			||||
use Illuminate\Support\Facades\Redis;
 | 
				
			||||
 | 
				
			||||
class SeckillController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('秒杀列表')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->grid());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function create(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('新建秒杀')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body($this->form());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a grid builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Grid
 | 
				
			||||
     */
 | 
				
			||||
    protected function grid()
 | 
				
			||||
    {
 | 
				
			||||
        $grid = new Grid(new Seckill);
 | 
				
			||||
 | 
				
			||||
        $grid->model()->latest();
 | 
				
			||||
 | 
				
			||||
        $grid->column('id');
 | 
				
			||||
 | 
				
			||||
        $grid->column('product.id', '商品ID');
 | 
				
			||||
        $grid->column('product.name', '商品名字')->display(function ($name) {
 | 
				
			||||
            return str_limit($name, 30);
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('product.thumb', '商品图')->display(function ($thumb) {
 | 
				
			||||
            return image($thumb);
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $grid->column('price', '秒杀价');
 | 
				
			||||
        $grid->column('number', '秒杀数量');
 | 
				
			||||
        $grid->column('start_at', '开始时间');
 | 
				
			||||
        $grid->column('end_at', '结束时间');
 | 
				
			||||
        $grid->column('rollback_count', '回滚量');
 | 
				
			||||
        $grid->column('is_rollback', '是否回滚数量')->display(function ($is) {
 | 
				
			||||
            return $is ? '是' : '否';
 | 
				
			||||
        });
 | 
				
			||||
        $grid->column('created_at', '创建时间');
 | 
				
			||||
        $grid->column('updated_at', '修改时间');
 | 
				
			||||
 | 
				
			||||
        $grid->actions(function (Grid\Displayers\Actions $actions) {
 | 
				
			||||
 | 
				
			||||
            $actions->disableView();
 | 
				
			||||
            $actions->disableEdit();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        return $grid;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Make a form builder.
 | 
				
			||||
     *
 | 
				
			||||
     * @return Form
 | 
				
			||||
     */
 | 
				
			||||
    protected function form()
 | 
				
			||||
    {
 | 
				
			||||
        $form = new Form(new Seckill);
 | 
				
			||||
 | 
				
			||||
        $categories = Category::selectOrderAll();
 | 
				
			||||
        $form->select('category_id', '分类')
 | 
				
			||||
             ->options($categories)
 | 
				
			||||
             ->load('product_id', admin_url('api/products'));
 | 
				
			||||
        $form->select('product_id', '秒杀商品');
 | 
				
			||||
 | 
				
			||||
        $form->number('price', '秒杀价')
 | 
				
			||||
             ->default(1);
 | 
				
			||||
        $form->number('number', '秒杀数量')
 | 
				
			||||
             ->default(1)
 | 
				
			||||
             ->help('保证商品的库存数量大于此数量,会从库存中减去');
 | 
				
			||||
 | 
				
			||||
        $now = Carbon::now();
 | 
				
			||||
        $form->datetime('start_at', '开始时间')
 | 
				
			||||
             ->default($now->format('Y-m-d H:00:00'));
 | 
				
			||||
        $form->datetime('end_at', '结束时间')
 | 
				
			||||
             ->default($now->addHour(2)->format('Y-m-d H:00:00'))
 | 
				
			||||
             ->rules('required|date|after_or_equal:start_at');
 | 
				
			||||
 | 
				
			||||
        return $form;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * @param Request $request
 | 
				
			||||
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
 | 
				
			||||
     * @throws \Exception
 | 
				
			||||
     */
 | 
				
			||||
    public function store(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        $number = $request->input('number', 0);
 | 
				
			||||
        $product = Product::query()->findOrFail($request->input('product_id'));
 | 
				
			||||
 | 
				
			||||
        if ($number > $product->count) {
 | 
				
			||||
 | 
				
			||||
            return back()->withInput()->withErrors(['number' => '秒杀数量不能大于库存数量']);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        DB::beginTransaction();
 | 
				
			||||
 | 
				
			||||
        try {
 | 
				
			||||
 | 
				
			||||
            $attributes = $request->only(['category_id', 'product_id', 'price', 'number', 'start_at', 'end_at']);
 | 
				
			||||
            Seckill::create($attributes);
 | 
				
			||||
 | 
				
			||||
            // 减去库存数量
 | 
				
			||||
            $product->decrement('count', $number);
 | 
				
			||||
 | 
				
			||||
        } catch (\Error $e) {
 | 
				
			||||
 | 
				
			||||
            DB::rollBack();
 | 
				
			||||
            return back()->withInput()->withErrors(['category_id' => $e->getMessage()]);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        DB::commit();
 | 
				
			||||
 | 
				
			||||
        admin_toastr('添加成功');
 | 
				
			||||
        return back();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Remove the specified resource from storage.
 | 
				
			||||
     *
 | 
				
			||||
     * @param int $id
 | 
				
			||||
     * @return \Illuminate\Http\Response
 | 
				
			||||
     * @throws \Exception
 | 
				
			||||
     */
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $seckill Seckill
 | 
				
			||||
         * @var $product Product
 | 
				
			||||
         */
 | 
				
			||||
        $seckill = Seckill::query()->findOrFail($id);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 如果已经到了开始抢购时间,就不能删除了
 | 
				
			||||
        $now = Carbon::now();
 | 
				
			||||
        $startTime = Carbon::make($seckill->start_at);
 | 
				
			||||
        $endTime = Carbon::make($seckill->end_at);
 | 
				
			||||
 | 
				
			||||
        // 如果正处于抢购的时间,不允许删除
 | 
				
			||||
        if ($now->gte($startTime) && $now->lte($endTime)) {
 | 
				
			||||
 | 
				
			||||
            return response()->json([
 | 
				
			||||
                'status' => false,
 | 
				
			||||
                'message' => '秒杀已经开始,不能删除',
 | 
				
			||||
            ]);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        DB::beginTransaction();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        try {
 | 
				
			||||
 | 
				
			||||
            // 先把 redis 数据删除掉
 | 
				
			||||
            // 虽然过期会自动清理,但是如果用户是
 | 
				
			||||
            // 删除还没有开始的秒杀,只能在这里手动清理
 | 
				
			||||
            \Redis::connection()->del([$seckill->getRedisModelKey(), $seckill->getRedisQueueKey()]);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
            $seckill->delete();
 | 
				
			||||
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => false,
 | 
				
			||||
                'message' => trans('admin.delete_succeeded'),
 | 
				
			||||
            ];
 | 
				
			||||
 | 
				
			||||
        } catch (\Exception $e) {
 | 
				
			||||
 | 
				
			||||
            $data = [
 | 
				
			||||
                'status'  => true,
 | 
				
			||||
                'message' => trans('admin.delete_failed'),
 | 
				
			||||
            ];
 | 
				
			||||
            DB::rollBack();
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        DB::commit();
 | 
				
			||||
        return response()->json($data);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,99 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Enums\SettingKeyEnum;
 | 
				
			||||
use App\Models\Setting;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use Encore\Admin\Controllers\HasResourceActions;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
use Encore\Admin\Grid;
 | 
				
			||||
use Encore\Admin\Layout\Content;
 | 
				
			||||
use Encore\Admin\Layout\Row;
 | 
				
			||||
use Encore\Admin\Show;
 | 
				
			||||
use Encore\Admin\Widgets\Box;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class SettingController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    use HasResourceActions;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Index interface.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Content $content
 | 
				
			||||
     * @return Content
 | 
				
			||||
     */
 | 
				
			||||
    public function index(Content $content)
 | 
				
			||||
    {
 | 
				
			||||
        return $content
 | 
				
			||||
            ->header('配置列表')
 | 
				
			||||
            ->description('')
 | 
				
			||||
            ->body(function (Row $row) {
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                $settingKeys = $this->getSettingKeys();
 | 
				
			||||
 | 
				
			||||
                $settings = [];
 | 
				
			||||
                foreach ($settingKeys as $key) {
 | 
				
			||||
 | 
				
			||||
                    $settings[$key] = setting(new SettingKeyEnum($key));
 | 
				
			||||
                }
 | 
				
			||||
 | 
				
			||||
                $form = new \Encore\Admin\Widgets\Form($settings);
 | 
				
			||||
                $form->action(admin_url('settings'));
 | 
				
			||||
                $form->method('POST');
 | 
				
			||||
                $form->hidden('_token', csrf_token());
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                $form->text(SettingKeyEnum::USER_INIT_PASSWORD, '会员初始密码')->required();
 | 
				
			||||
                $form->decimal(SettingKeyEnum::UN_PAY_CANCEL_TIME, '订单自动取消时间')->required()->help('单位:分钟');
 | 
				
			||||
                $form->decimal(SettingKeyEnum::POST_AMOUNT, '邮费')->required()->help('设置为 0 免邮');
 | 
				
			||||
 | 
				
			||||
                $states = [
 | 
				
			||||
                    'on'  => ['value' => 1, 'text' => '打开', 'color' => 'success'],
 | 
				
			||||
                    'off' => ['value' => 0, 'text' => '关闭', 'color' => 'danger'],
 | 
				
			||||
                ];
 | 
				
			||||
 | 
				
			||||
                $seckillStatus = \setting(new SettingKeyEnum(SettingKeyEnum::IS_OPEN_SECKILL)) ? '开启' : '关闭';
 | 
				
			||||
                $seckillHelp = "秒杀现在是 {$seckillStatus} 状态";
 | 
				
			||||
                $form->switch(SettingKeyEnum::IS_OPEN_SECKILL, '是否开启秒杀')->states($states)->help($seckillHelp);
 | 
				
			||||
 | 
				
			||||
                $row->column(12, new Box('网站配置', $form));
 | 
				
			||||
            });
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function store(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        // 对秒杀这个特殊的 key 做处理
 | 
				
			||||
        $val = strtolower($request->input(SettingKeyEnum::IS_OPEN_SECKILL)) == 'on' ? 1 : 0;
 | 
				
			||||
        $request->offsetSet(SettingKeyEnum::IS_OPEN_SECKILL, $val);
 | 
				
			||||
 | 
				
			||||
        $this->validate($request, [
 | 
				
			||||
            SettingKeyEnum::USER_INIT_PASSWORD => 'required|string',
 | 
				
			||||
            SettingKeyEnum::UN_PAY_CANCEL_TIME => 'required|integer|min:0',
 | 
				
			||||
            SettingKeyEnum::IS_OPEN_SECKILL => 'required|int|in:0,1',
 | 
				
			||||
            SettingKeyEnum::POST_AMOUNT => 'required|numeric|min:0',
 | 
				
			||||
        ]);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $settingKeys = $this->getSettingKeys();
 | 
				
			||||
        foreach ($settingKeys as $key) {
 | 
				
			||||
 | 
				
			||||
            Setting::query()->where('key', $key)->update(['value' => $request->input($key)]);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        admin_success('修改成功');
 | 
				
			||||
        return back();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    private function getSettingKeys()
 | 
				
			||||
    {
 | 
				
			||||
        return [
 | 
				
			||||
            SettingKeyEnum::USER_INIT_PASSWORD,
 | 
				
			||||
            SettingKeyEnum::UN_PAY_CANCEL_TIME,
 | 
				
			||||
            SettingKeyEnum::POST_AMOUNT,
 | 
				
			||||
            SettingKeyEnum::IS_OPEN_SECKILL,
 | 
				
			||||
        ];
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,39 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Exceptions\UploadException;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Services\UploadServe;
 | 
				
			||||
use Illuminate\Support\Facades\Storage;
 | 
				
			||||
 | 
				
			||||
class UploadController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * @param UploadServe $uploadServe
 | 
				
			||||
     * @return array
 | 
				
			||||
     */
 | 
				
			||||
    public function uploadByEditor(UploadServe $uploadServe)
 | 
				
			||||
    {
 | 
				
			||||
        $disk = 'public';
 | 
				
			||||
 | 
				
			||||
        try {
 | 
				
			||||
            $files = $uploadServe->setFileInput('pictures')
 | 
				
			||||
                                 ->setMaxSize('10M')
 | 
				
			||||
                                 ->setExtensions(['jpg', 'jpeg', 'png', 'bmp', 'gif'])
 | 
				
			||||
                                 ->validate()
 | 
				
			||||
                                 ->storeMulti('upload/editor', compact('disk'));
 | 
				
			||||
 | 
				
			||||
            $files = collect($files)->map(function ($file) use ($disk) {
 | 
				
			||||
                return Storage::disk($disk)->url($file);
 | 
				
			||||
            })->all();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        } catch (UploadException $e) {
 | 
				
			||||
 | 
				
			||||
            return ['errno' => 1, 'msg' => $e->getMessage()];
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return ['errno' => 0, 'data' => $files];
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,44 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Extensions;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use Illuminate\Contracts\Support\Renderable;
 | 
				
			||||
 | 
				
			||||
class Div implements Renderable
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
    protected $id;
 | 
				
			||||
    protected $width = '100%';
 | 
				
			||||
    protected $height = '300px';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Div constructor.
 | 
				
			||||
     *
 | 
				
			||||
     * @param        $id
 | 
				
			||||
     * @param string $width
 | 
				
			||||
     * @param string $height
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct($id, $width = null, $height = null)
 | 
				
			||||
    {
 | 
				
			||||
        $this->id = $id;
 | 
				
			||||
 | 
				
			||||
        if (! is_null($width)) {
 | 
				
			||||
            $this->width = $width;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if (! is_null($height)) {
 | 
				
			||||
            $this->height = $height;
 | 
				
			||||
        }
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function render()
 | 
				
			||||
    {
 | 
				
			||||
        return <<<div
 | 
				
			||||
<div id="{$this->id}" style="width: {$this->width}; height: {$this->height}"></div>
 | 
				
			||||
div;
 | 
				
			||||
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,56 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Extensions;
 | 
				
			||||
 | 
				
			||||
use Illuminate\Support\Collection;
 | 
				
			||||
 | 
				
			||||
use Encore\Admin\Form\Field;
 | 
				
			||||
 | 
				
			||||
class WangEditor extends Field
 | 
				
			||||
{
 | 
				
			||||
    protected $view = 'admin.wang-editor';
 | 
				
			||||
 | 
				
			||||
    protected static $css = [
 | 
				
			||||
        '/vendor/wangEditor-3.1.1/release/wangEditor.css',
 | 
				
			||||
    ];
 | 
				
			||||
 | 
				
			||||
    protected static $js = [
 | 
				
			||||
        '/vendor/wangEditor-3.1.1/release/wangEditor.js',
 | 
				
			||||
    ];
 | 
				
			||||
 | 
				
			||||
    public function render()
 | 
				
			||||
    {
 | 
				
			||||
        $name = $this->formatName($this->column);
 | 
				
			||||
        $token = csrf_token();
 | 
				
			||||
        $url = admin_base_path('upload/editor');
 | 
				
			||||
 | 
				
			||||
        $this->script = <<<EOT
 | 
				
			||||
 | 
				
			||||
var E = window.wangEditor
 | 
				
			||||
var editor = new E('#{$this->id}');
 | 
				
			||||
editor.customConfig.zIndex = 0
 | 
				
			||||
editor.customConfig.uploadFileName = 'pictures[]'
 | 
				
			||||
// 配置服务器端地址
 | 
				
			||||
editor.customConfig.uploadImgServer = '{$url}'
 | 
				
			||||
editor.customConfig.uploadImgParams = {
 | 
				
			||||
    _token: '{$token}'
 | 
				
			||||
}
 | 
				
			||||
// 文件改变添加内容到隐藏域
 | 
				
			||||
editor.customConfig.onchange = function (html) {
 | 
				
			||||
    $('input[name=\'$name\']').val(html);
 | 
				
			||||
}
 | 
				
			||||
// 监听上传错误
 | 
				
			||||
editor.customConfig.uploadImgHooks = {
 | 
				
			||||
    fail: function (xhr, editor) {
 | 
				
			||||
        var response = $.parseJSON(xhr.response);
 | 
				
			||||
        
 | 
				
			||||
        alert(response.msg);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
editor.create()
 | 
				
			||||
 | 
				
			||||
EOT;
 | 
				
			||||
        return parent::render();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,26 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Transforms;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Enums\OrderPayTypeEnum;
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Enums\OrderTypeEnum;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
 | 
				
			||||
class OrderPayTypeTransform implements Transform
 | 
				
			||||
{
 | 
				
			||||
    public static function trans($type)
 | 
				
			||||
    {
 | 
				
			||||
        $text = '';
 | 
				
			||||
 | 
				
			||||
        if ($type == OrderPayTypeEnum::ALI) {
 | 
				
			||||
            $text = '支付宝';
 | 
				
			||||
        } elseif ($type == OrderPayTypeEnum::WECHAT) {
 | 
				
			||||
            $text = '微信';
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return $text;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,33 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Transforms;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Enums\OrderTypeEnum;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
 | 
				
			||||
class OrderShipStatusTransform implements Transform
 | 
				
			||||
{
 | 
				
			||||
    public static function trans($status)
 | 
				
			||||
    {
 | 
				
			||||
        switch ($status) {
 | 
				
			||||
 | 
				
			||||
            case OrderShipStatusEnum::PENDING:
 | 
				
			||||
                $text = '未发货';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderShipStatusEnum::DELIVERED:
 | 
				
			||||
                $text = '待收货';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderShipStatusEnum::RECEIVED:
 | 
				
			||||
                $text = '已收货';
 | 
				
			||||
                break;
 | 
				
			||||
            default:
 | 
				
			||||
                $text = '未知状态';
 | 
				
			||||
                break;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return $text;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,45 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Transforms;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Enums\OrderTypeEnum;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
 | 
				
			||||
class OrderStatusTransform implements Transform
 | 
				
			||||
{
 | 
				
			||||
    public static function trans($status)
 | 
				
			||||
    {
 | 
				
			||||
        switch ($status) {
 | 
				
			||||
 | 
				
			||||
            case OrderStatusEnum::UN_PAY_CANCEL:
 | 
				
			||||
                $text = '取消';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderStatusEnum::REFUND:
 | 
				
			||||
                $text = '退款';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderStatusEnum::APPLY_REFUND:
 | 
				
			||||
                $text = '申请退款';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderStatusEnum::UN_PAY:
 | 
				
			||||
                $text = '未支付';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderStatusEnum::PAID:
 | 
				
			||||
                $text = '已支付';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderStatusEnum::TIMEOUT_CANCEL:
 | 
				
			||||
                $text = '超时未付款系统自动取消';
 | 
				
			||||
                break;
 | 
				
			||||
            case OrderStatusEnum::COMPLETED:
 | 
				
			||||
                $text = '完成';
 | 
				
			||||
                break;
 | 
				
			||||
            default:
 | 
				
			||||
                $text = '未知状态';
 | 
				
			||||
                break;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return $text;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,25 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Transforms;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Enums\OrderTypeEnum;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
 | 
				
			||||
class OrderTypeTransform implements Transform
 | 
				
			||||
{
 | 
				
			||||
    public static function trans($type)
 | 
				
			||||
    {
 | 
				
			||||
        $text = '未知';
 | 
				
			||||
 | 
				
			||||
        if ($type == OrderTypeEnum::COMMON) {
 | 
				
			||||
            $text = '普通订单';
 | 
				
			||||
        } elseif ($type == OrderTypeEnum::SEC_KILL) {
 | 
				
			||||
            $text = '秒杀订单';
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return $text;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,9 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Transforms;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
interface Transform
 | 
				
			||||
{
 | 
				
			||||
    public static function trans($trans);
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,24 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Transforms;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Enums\UserSexEnum;
 | 
				
			||||
use App\Enums\UserStatusEnum;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
 | 
				
			||||
class UserSexTransform implements Transform
 | 
				
			||||
{
 | 
				
			||||
    public static function trans($sex)
 | 
				
			||||
    {
 | 
				
			||||
        $text = '未知';
 | 
				
			||||
 | 
				
			||||
        if ($sex == UserSexEnum::MAN) {
 | 
				
			||||
            $text = '男';
 | 
				
			||||
        } elseif ($sex == UserSexEnum::WOMAN) {
 | 
				
			||||
            $text = '女';
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return $text;
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,16 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Admin\Transforms;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Enums\UserStatusEnum;
 | 
				
			||||
 | 
				
			||||
class YesNoTransform implements Transform
 | 
				
			||||
{
 | 
				
			||||
    public static function trans($is)
 | 
				
			||||
    {
 | 
				
			||||
        return $is
 | 
				
			||||
            ? "<i style='color: green;' class=\"fa fa-check-circle\" aria-hidden=\"true\"></i>"
 | 
				
			||||
            : "<i style='color: red;' class=\"fa fa-times\" aria-hidden=\"true\"></i>";
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,8 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Admin\Extensions\WangEditor;
 | 
				
			||||
use Encore\Admin\Form;
 | 
				
			||||
 | 
				
			||||
Form::forget(['map', 'editor']);
 | 
				
			||||
Form::extend('editor', WangEditor::class);
 | 
				
			||||
@ -0,0 +1,76 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
use App\Admin\Controllers\CouponLogController;
 | 
				
			||||
use Illuminate\Routing\Router;
 | 
				
			||||
 | 
				
			||||
Admin::registerAuthRoutes();
 | 
				
			||||
 | 
				
			||||
Route::group([
 | 
				
			||||
    'prefix'        => config('admin.route.prefix'),
 | 
				
			||||
    'namespace'     => config('admin.route.namespace'),
 | 
				
			||||
    'middleware'    => config('admin.route.middleware'),
 | 
				
			||||
], function (Router $router) {
 | 
				
			||||
 | 
				
			||||
    $router->fallback('HomeController@noFound');
 | 
				
			||||
    $router->get('/', 'HomeController@index');
 | 
				
			||||
 | 
				
			||||
    // 覆盖默认的用户管理
 | 
				
			||||
    $router->get('auth/users', 'AdminController@index');
 | 
				
			||||
    // 覆盖默认的操作日志
 | 
				
			||||
    $router->get('auth/logs', 'AdminController@indexLogs');
 | 
				
			||||
 | 
				
			||||
    // 系统的配置
 | 
				
			||||
    $router->resource('settings', 'SettingController')->only('index', 'store');
 | 
				
			||||
 | 
				
			||||
    // 商品上架下架
 | 
				
			||||
    $router->get('products/{id}/push', 'ProductController@pushProduct');
 | 
				
			||||
 | 
				
			||||
    // 分类
 | 
				
			||||
    // 商品
 | 
				
			||||
    // 秒杀的商品管理
 | 
				
			||||
    $router->resource('categories', 'CategoryController');
 | 
				
			||||
    $router->resource('products', 'ProductController');
 | 
				
			||||
    $router->resource('seckills', 'SeckillController')->only('index', 'create', 'store', 'destroy');
 | 
				
			||||
 | 
				
			||||
    // 商品发货
 | 
				
			||||
    // 管理员帮忙确认收货
 | 
				
			||||
    $router->post('orders/{order}/ship', 'OrderController@ship');
 | 
				
			||||
    $router->patch('orders/{order}/shipped', 'OrderController@confirmShip');
 | 
				
			||||
 | 
				
			||||
    // 退款
 | 
				
			||||
    // 订单
 | 
				
			||||
    // 评论
 | 
				
			||||
    $router->get('orders/{order}/refund', 'OrderController@refund');
 | 
				
			||||
    $router->resource('orders', 'OrderController');
 | 
				
			||||
    $router->resource('comments', 'CommentController');
 | 
				
			||||
 | 
				
			||||
    // 会员管理
 | 
				
			||||
    $router->resource('users', 'UserController');
 | 
				
			||||
 | 
				
			||||
    // 积分日志
 | 
				
			||||
    $router->get('score_logs', 'ScoreLogController@index');
 | 
				
			||||
    // 用户购物车数据
 | 
				
			||||
    $router->get('cars', 'CarController@index');
 | 
				
			||||
    // 用户收藏数据
 | 
				
			||||
    $router->get('user_like_products', 'ProductLikeController@index');
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    // 积分规则, 积分等级
 | 
				
			||||
    $router->resource('score_rules', 'ScoreRuleController');
 | 
				
			||||
    $router->resource('levels', 'LevelController');
 | 
				
			||||
 | 
				
			||||
    // 优惠券管理
 | 
				
			||||
    $router->resource('coupon_templates', 'CouponTemplateController');
 | 
				
			||||
    // 优惠券
 | 
				
			||||
    $router->resource('coupon_logs', 'CouponLogController')->only('index');
 | 
				
			||||
    // 优惠券兑换码
 | 
				
			||||
    $router->resource('coupon_codes', 'CouponCodeController')->only('index', 'create', 'store', 'destroy');
 | 
				
			||||
 | 
				
			||||
    // 发布文章通知
 | 
				
			||||
    $router->resource('article_notifications', 'ArticleNotificationController')->only('index', 'create', 'store', 'show', 'destroy');
 | 
				
			||||
 | 
				
			||||
    // 富文本图片上传
 | 
				
			||||
    $router->post('upload/editor', 'UploadController@uploadByEditor');
 | 
				
			||||
    // 通过分类异步加载商品下拉列表
 | 
				
			||||
    $router->get('api/products', 'CategoryController@getProducts');
 | 
				
			||||
});
 | 
				
			||||
@ -0,0 +1,91 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\SearchAble\ElasticSearchTrait;
 | 
				
			||||
use Elasticsearch\ClientBuilder;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Illuminate\Database\Eloquent\Collection;
 | 
				
			||||
 | 
				
			||||
class AddShopToEsSearchCommand extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'add:shop-to-search';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = '把商品添加到全文索引';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        try {
 | 
				
			||||
            ElasticSearchTrait::client()->ping([
 | 
				
			||||
                'client' => [
 | 
				
			||||
                    'timeout' => 5,
 | 
				
			||||
                    'connect_timeout' => 5
 | 
				
			||||
                ]
 | 
				
			||||
            ]);
 | 
				
			||||
        } catch (\Exception $exception) {
 | 
				
			||||
            
 | 
				
			||||
            $this->info($exception->getMessage());
 | 
				
			||||
            $this->info('无法连接到 elasticsearch 服务器,请配置 config/elasticsearch.php 文件');
 | 
				
			||||
            $this->info('默认使用 MySQL 的模糊搜索');
 | 
				
			||||
            $this->info('配置完毕后可运行: php artisan add:shop-to-search 添加索引');
 | 
				
			||||
            return;
 | 
				
			||||
        }
 | 
				
			||||
        
 | 
				
			||||
        
 | 
				
			||||
        // 新建商品索引
 | 
				
			||||
        if (Product::indexExists()) {
 | 
				
			||||
            
 | 
				
			||||
            Product::deleteIndex();
 | 
				
			||||
            $this->info('删除索引');
 | 
				
			||||
        }
 | 
				
			||||
        Product::createIndex();
 | 
				
			||||
        $this->info('新建索引成功');
 | 
				
			||||
        
 | 
				
			||||
        
 | 
				
			||||
        // 开始导入数据
 | 
				
			||||
        $query = Product::query();
 | 
				
			||||
        
 | 
				
			||||
        $count = $query->count();
 | 
				
			||||
        $handle = 0;
 | 
				
			||||
        
 | 
				
			||||
        $query->with('category')->chunk(1000, function (Collection $models) use ($count, &$handle) {
 | 
				
			||||
            
 | 
				
			||||
            $models->map(function (Product $product) use ($count, &$handle) {
 | 
				
			||||
                
 | 
				
			||||
                $product->addToIndex($product->getSearchData());
 | 
				
			||||
                
 | 
				
			||||
                ++ $handle;
 | 
				
			||||
                echo "\r {$handle}/$count";
 | 
				
			||||
            });
 | 
				
			||||
        });
 | 
				
			||||
        
 | 
				
			||||
        echo PHP_EOL;
 | 
				
			||||
        $this->info('索引生成完毕');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,40 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
 | 
				
			||||
class BaseCommand extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:base';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'don`t use';
 | 
				
			||||
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function execShellWithPrint($command)
 | 
				
			||||
    {
 | 
				
			||||
 | 
				
			||||
        $this->info('----------');
 | 
				
			||||
        $this->info($command);
 | 
				
			||||
 | 
				
			||||
        $output = shell_exec($command);
 | 
				
			||||
 | 
				
			||||
        $this->info($output);
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,44 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
class CacheOptimize extends BaseCommand
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:cache';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'Cache routing, configure information, to speed up the website running speed';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 执行缓存命令
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        // 优化配置
 | 
				
			||||
        $this->call('config:cache');
 | 
				
			||||
        // 优化路由
 | 
				
			||||
        $this->call('route:cache');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,42 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
class ClearCache extends BaseCommand
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:clear';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'clear moon:cache cache information';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 清除所有缓存
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        $this->call('config:clear');
 | 
				
			||||
        $this->call('route:clear');
 | 
				
			||||
        $this->call('view:clear');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,66 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use Illuminate\Filesystem\Filesystem;
 | 
				
			||||
use Illuminate\Support\Facades\Storage;
 | 
				
			||||
 | 
				
			||||
class CopyFile extends BaseCommand
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:copy';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'Copy all upload static resources';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * @var Filesystem
 | 
				
			||||
     */
 | 
				
			||||
    protected $filesystem;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @param Filesystem $filesystem
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct(Filesystem $filesystem)
 | 
				
			||||
    {
 | 
				
			||||
        $this->filesystem = $filesystem;
 | 
				
			||||
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 把静态资源发布到 public/storage 目录
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        // 图片的静态目录
 | 
				
			||||
        $from = storage_path('app/resources/products');
 | 
				
			||||
        $to = storage_path('app/public/products');
 | 
				
			||||
        $this->filesystem->copyDirectory($from, $to);
 | 
				
			||||
 | 
				
			||||
        // 默认头像
 | 
				
			||||
        $from = storage_path('app/resources/avatars');
 | 
				
			||||
        $to = storage_path('app/public/avatars');
 | 
				
			||||
        $this->filesystem->copyDirectory($from, $to);
 | 
				
			||||
 | 
				
			||||
        // 默认头像
 | 
				
			||||
        $from = storage_path('app/resources/images');
 | 
				
			||||
        $to = storage_path('app/public/images');
 | 
				
			||||
        $this->filesystem->copyDirectory($from, $to);
 | 
				
			||||
 | 
				
			||||
        $this->info('copy file success');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,59 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Models\SiteCount;
 | 
				
			||||
use App\Services\SiteCountService;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Illuminate\Support\Facades\Cache;
 | 
				
			||||
 | 
				
			||||
class CountSite extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:count-site';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = '统计数据,直接执行会统计到昨天';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @param SiteCountService $service
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle(SiteCountService $service)
 | 
				
			||||
    {
 | 
				
			||||
        // 每天凌晨统计昨天的数据
 | 
				
			||||
        $date = Carbon::now()->subDay(1)->toDateString();
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * 防止一天运行多次,所以采用增加
 | 
				
			||||
         *
 | 
				
			||||
         * @var $site SiteCount
 | 
				
			||||
         */
 | 
				
			||||
        $site = SiteCount::query()->firstOrNew(compact('date'));
 | 
				
			||||
        $site = $service->syncByCache($site, true);
 | 
				
			||||
        $site->save();
 | 
				
			||||
 | 
				
			||||
        createSystemLog('系统统计站点数据', $site->toArray());
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,53 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Services\ScoreLogServe;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Illuminate\Support\Facades\Cache;
 | 
				
			||||
 | 
				
			||||
class DelExpireScoreData extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:del-score-data';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'Command description';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     * @throws \Psr\SimpleCache\InvalidArgumentException
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        // 获取昨天的日期
 | 
				
			||||
        $yesterday = Carbon::today()->subDay()->toDateString();
 | 
				
			||||
 | 
				
			||||
        $serve = new ScoreLogServe();
 | 
				
			||||
 | 
				
			||||
        Cache::delete($serve->loginKey($yesterday));
 | 
				
			||||
 | 
				
			||||
        createSystemLog("系统删除{$yesterday}过期积分统计数据", ['date' => $yesterday]);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,92 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Models\Seckill;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Illuminate\Support\Facades\Redis;
 | 
				
			||||
 | 
				
			||||
class DelExpireSecKill extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:del-seckills';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 删除过期的秒杀
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'delete expire seckills';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        $rollbacks = collect();
 | 
				
			||||
 | 
				
			||||
        // 查出已经过期确没有回滚过的秒杀,
 | 
				
			||||
        Seckill::query()
 | 
				
			||||
               ->where('is_rollback', 0)
 | 
				
			||||
               ->where('end_at', '<', Carbon::now()->toDateTimeString())
 | 
				
			||||
               ->get()
 | 
				
			||||
               ->map(function (Seckill $seckill) use ($rollbacks) {
 | 
				
			||||
 | 
				
			||||
                   // 1. 回滚数量到商品
 | 
				
			||||
                   // 2. 设置为过期
 | 
				
			||||
                   $product = $seckill->product()->first();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                   // 获取 redis 数量
 | 
				
			||||
                   $jsonSeckill = Redis::get($seckill->getRedisModelKey());
 | 
				
			||||
                   $redisSeckill = json_decode($jsonSeckill, true);
 | 
				
			||||
                   // 获取剩余秒杀量
 | 
				
			||||
                   $surplus = Redis::llen($seckill->getRedisQueueKey());
 | 
				
			||||
 | 
				
			||||
                   // 恢复剩余的库存量
 | 
				
			||||
                   // 恢复库存数量
 | 
				
			||||
                   if ($redisSeckill['sale_count'] != 0) {
 | 
				
			||||
                       $product->increment('sale_count', $redisSeckill['sale_count']);
 | 
				
			||||
                   }
 | 
				
			||||
 | 
				
			||||
                   if ($surplus != 0) {
 | 
				
			||||
                       $product->increment('count', $surplus);
 | 
				
			||||
                   }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                   // 同步 redis 数据到数据库中
 | 
				
			||||
                   $seckill->sale_count += $redisSeckill['sale_count'];
 | 
				
			||||
                   $seckill->rollback_count += $surplus;
 | 
				
			||||
                   $seckill->is_rollback = 1;
 | 
				
			||||
                   $seckill->save();
 | 
				
			||||
 | 
				
			||||
                   $rollbacks->push($seckill);
 | 
				
			||||
 | 
				
			||||
                   // 删除掉秒杀数据
 | 
				
			||||
                   $ids = Redis::connection()->keys("seckills:{$seckill->id}:*");
 | 
				
			||||
                   Redis::del($ids);
 | 
				
			||||
               });
 | 
				
			||||
 | 
				
			||||
        if ($rollbacks->isNotEmpty()) {
 | 
				
			||||
 | 
				
			||||
            createSystemLog('系统回滚秒杀数据', $rollbacks->toArray());
 | 
				
			||||
        }
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,46 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use Illuminate\Support\Facades\Storage;
 | 
				
			||||
 | 
				
			||||
class DeleteFile extends BaseCommand
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:delete';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'Delete all upload static resources';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 删除静态资源文件
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        Storage::deleteDirectory('public' . DIRECTORY_SEPARATOR . config('web.upload.list'));
 | 
				
			||||
        Storage::deleteDirectory('public' . DIRECTORY_SEPARATOR . config('web.upload.detail'));
 | 
				
			||||
 | 
				
			||||
        $this->info('delete file success');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,56 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Illuminate\Database\Eloquent\Model;
 | 
				
			||||
use Illuminate\Support\Collection;
 | 
				
			||||
 | 
				
			||||
class ExportDatabaseCommand extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:export';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = '导出数据到 json 文件';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        $users = User::query()
 | 
				
			||||
                     ->oldest()
 | 
				
			||||
                     ->get()
 | 
				
			||||
                     ->transform(function (User $user) {
 | 
				
			||||
 | 
				
			||||
                         // 先排除自身主键
 | 
				
			||||
                         $user->offsetUnset($user->getKeyName());
 | 
				
			||||
 | 
				
			||||
                         return $user;
 | 
				
			||||
                     });
 | 
				
			||||
 | 
				
			||||
        file_put_contents(\UsersTableSeeder::DATA_PATH, $users->toJson(JSON_UNESCAPED_UNICODE));
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,71 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
 | 
				
			||||
class InstallShop extends BaseCommand
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:install';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = '初始化商城项目';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 安装商城
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        $this->call('key:generate');
 | 
				
			||||
        // 删除上一次保留的数据表
 | 
				
			||||
        $this->call('migrate:reset');
 | 
				
			||||
        // 删除上一次保留的文件
 | 
				
			||||
        $this->call('moon:delete');
 | 
				
			||||
    
 | 
				
			||||
    
 | 
				
			||||
        Product::$addToSearch = false;
 | 
				
			||||
    
 | 
				
			||||
        /****************************************
 | 
				
			||||
         * 1. 迁移数据表
 | 
				
			||||
         * 2. 数据库迁移
 | 
				
			||||
         * 3. 复制静态资源
 | 
				
			||||
         * 4. 创建软链接
 | 
				
			||||
         */
 | 
				
			||||
        $this->call('migrate');
 | 
				
			||||
        $this->call('db:seed');
 | 
				
			||||
        $this->call('moon:copy');
 | 
				
			||||
        $this->call('storage:link');
 | 
				
			||||
 | 
				
			||||
        // 更新首页数据,防止上一次遗留
 | 
				
			||||
        $this->call('moon:update-home');
 | 
				
			||||
    
 | 
				
			||||
        // 生成全文索引
 | 
				
			||||
        $this->call('add:shop-to-search');
 | 
				
			||||
 | 
				
			||||
        // 直接开启监听队列
 | 
				
			||||
        // $this->info('queue starting please don`t close cmd windows!!!');
 | 
				
			||||
        // $this->call('queue:work', ['--tries' => '3']);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,55 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Mail\SubscribesNotice;
 | 
				
			||||
use App\Models\Subscribe;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Mail;
 | 
				
			||||
 | 
				
			||||
class SendSubscribeEmail extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:send-subscribes';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'Command description';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        $mails = Subscribe::query()->where('is_subscribe', 1)->pluck('email');
 | 
				
			||||
 | 
				
			||||
        $mails->map(function ($realMail) {
 | 
				
			||||
 | 
				
			||||
            $email = encrypt($realMail);
 | 
				
			||||
            $url = route('site.email', compact('email'));
 | 
				
			||||
            // 不要一次 to 多个用户,会暴露其他人的邮箱
 | 
				
			||||
            Mail::to($realMail)->send(new SubscribesNotice($url));
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        createSystemLog('系统发送订阅消息, 发送的用户:' . $mails->implode(', '), $mails->toArray());
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,58 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Illuminate\Support\Facades\Cache;
 | 
				
			||||
 | 
				
			||||
class SyncProducViewCommand extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:sync-product_view';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'Command description';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        $yesterday = Carbon::yesterday()->toDateString();
 | 
				
			||||
 | 
				
			||||
        $products = Product::query()->where('today_has_view', 1)->get();
 | 
				
			||||
 | 
				
			||||
        $products->map(function (Product $product) use ($yesterday) {
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
            $viewCount = Cache::pull($product->getViewCountKey($yesterday), 0);
 | 
				
			||||
            $product->view_count += $viewCount;
 | 
				
			||||
            $product->today_has_view = 0;
 | 
				
			||||
            $product->save();
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        createSystemLog("系统同步{$yesterday}商品浏览量", ['date' => $yesterday]);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,45 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
class UninstallShop extends BaseCommand
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:uninstall';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = '卸载商城项目';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        $this->call('moon:clear');
 | 
				
			||||
        $this->call('migrate:reset');
 | 
				
			||||
 | 
				
			||||
        // delete all upload static resources
 | 
				
			||||
        $this->call('moon:delete');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,55 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console\Commands;
 | 
				
			||||
 | 
				
			||||
use App\Enums\HomeCacheEnum;
 | 
				
			||||
use App\Models\Category;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Utils\HomeCacheDataUtil;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Console\Command;
 | 
				
			||||
use Illuminate\Support\Facades\Cache;
 | 
				
			||||
 | 
				
			||||
class UpdateCacheHomeData extends Command
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The name and signature of the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $signature = 'moon:update-home';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * The console command description.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $description = 'Command description';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new command instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        parent::__construct();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Execute the console command.
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function handle()
 | 
				
			||||
    {
 | 
				
			||||
        // 每分钟有定时任务更新
 | 
				
			||||
        $ttl = 60 * 2;
 | 
				
			||||
 | 
				
			||||
        HomeCacheDataUtil::categories($ttl, true);
 | 
				
			||||
        HomeCacheDataUtil::hotProducts($ttl, true);
 | 
				
			||||
        HomeCacheDataUtil::latestProducts($ttl, true);
 | 
				
			||||
        HomeCacheDataUtil::users($ttl, true);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,60 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Console;
 | 
				
			||||
 | 
				
			||||
use App\Console\Commands\CountSite;
 | 
				
			||||
use App\Console\Commands\DelExpireScoreData;
 | 
				
			||||
use App\Console\Commands\DelExpireSecKill;
 | 
				
			||||
use App\Console\Commands\SendSubscribeEmail;
 | 
				
			||||
use App\Console\Commands\SyncProducViewCommand;
 | 
				
			||||
use App\Console\Commands\UpdateCacheHomeData;
 | 
				
			||||
use Illuminate\Console\Scheduling\Schedule;
 | 
				
			||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 | 
				
			||||
 | 
				
			||||
class Kernel extends ConsoleKernel
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The Artisan commands provided by your application.
 | 
				
			||||
     *
 | 
				
			||||
     * @var array
 | 
				
			||||
     */
 | 
				
			||||
    protected $commands = [
 | 
				
			||||
        //
 | 
				
			||||
    ];
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Define the application's command schedule.
 | 
				
			||||
     *
 | 
				
			||||
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    protected function schedule(Schedule $schedule)
 | 
				
			||||
    {
 | 
				
			||||
        // 每周六八点发送订阅邮件
 | 
				
			||||
        $schedule->command(SendSubscribeEmail::class)->saturdays()->at('8:00');
 | 
				
			||||
        // 每天统计注册人数, 销售数量
 | 
				
			||||
        $schedule->command(CountSite::class)->dailyAt('01:00');
 | 
				
			||||
        // 每小时执行一次, 回滚秒杀过期的数据
 | 
				
			||||
        $schedule->command(DelExpireSecKill::class)->hourly();
 | 
				
			||||
 | 
				
			||||
        // 每天夜里十二点执行删除昨天过期的积分数据
 | 
				
			||||
        $schedule->command(DelExpireScoreData::class)->dailyAt('00:00');
 | 
				
			||||
        // 每天夜里十二点同步商品浏览量
 | 
				
			||||
        $schedule->command(SyncProducViewCommand::class)->dailyAt('00:10');
 | 
				
			||||
 | 
				
			||||
        // 每分钟更新一次首页数据
 | 
				
			||||
        $schedule->command(UpdateCacheHomeData::class)->everyMinute();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Register the commands for the application.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    protected function commands()
 | 
				
			||||
    {
 | 
				
			||||
        $this->load(__DIR__.'/Commands');
 | 
				
			||||
 | 
				
			||||
        require base_path('routes/console.php');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,17 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class HomeCacheEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    // CHANGE: 修改 kye 名,防止旧版本 key 永久缓存不修改
 | 
				
			||||
    const SEC_KILL_DATA = 'home_page:sec_kill_data';
 | 
				
			||||
    const CATEGORIES = 'home_page:categories';
 | 
				
			||||
    const HOTTEST = 'home_page:hottest';
 | 
				
			||||
    const LATEST = 'home_page:latest';
 | 
				
			||||
    const USERS = 'home_page:users';
 | 
				
			||||
    const COUPON_TEMPLATES = 'home_page:templates';
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,12 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class OrderPayTypeEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    const ALI = 'ali';
 | 
				
			||||
    const WECHAT = 'wechat';
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,17 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class OrderShipStatusEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    // 未发货
 | 
				
			||||
    const PENDING = 1;
 | 
				
			||||
    // 待收货
 | 
				
			||||
    const DELIVERED = 2;
 | 
				
			||||
    // 已收货
 | 
				
			||||
    const RECEIVED = 3;
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,26 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class OrderStatusEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
    const UN_PAY_CANCEL = 0;
 | 
				
			||||
    // 未支付
 | 
				
			||||
    const UN_PAY = 1;
 | 
				
			||||
    // 已经支付
 | 
				
			||||
    const PAID = 2;
 | 
				
			||||
    // 订单完成
 | 
				
			||||
    const COMPLETED = 3;
 | 
				
			||||
 | 
				
			||||
    // 超时未支付
 | 
				
			||||
    const TIMEOUT_CANCEL = 4;
 | 
				
			||||
    // 申请退款
 | 
				
			||||
    const APPLY_REFUND = 5;
 | 
				
			||||
    // 退款
 | 
				
			||||
    const REFUND = 6;
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,12 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class OrderTypeEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    const COMMON = 1;
 | 
				
			||||
    const SEC_KILL = 2;
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,15 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class ScoreRuleIndexEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    const CONTINUE_LOGIN = 'continue_login';
 | 
				
			||||
    const VISITED_PRODUCT = 'visited_product';
 | 
				
			||||
    const COMPLETE_ORDER = 'complete_order';
 | 
				
			||||
    const LOGIN = 'login';
 | 
				
			||||
    const REGISTER = 'register';
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,14 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class SettingKeyEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    const USER_INIT_PASSWORD = 'user_init_password';
 | 
				
			||||
    const IS_OPEN_SECKILL = 'is_open_seckill';
 | 
				
			||||
    const UN_PAY_CANCEL_TIME = 'order_un_pay_auto_cancel_time';
 | 
				
			||||
    const POST_AMOUNT = 'post_amount';
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,25 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class SiteCountCacheEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    // 注册的 key
 | 
				
			||||
    const REGISTERED_COUNT = 'site_counts:registered_count';
 | 
				
			||||
    const GITHUB_REGISTERED_COUNT = 'site_counts:github_registered_count';
 | 
				
			||||
    const QQ_REGISTER_COUNT = 'site_counts:qq_registered_count';
 | 
				
			||||
    const WEIBO_REGISTER_COUNT = 'site_counts:weibo_registered_count';
 | 
				
			||||
    const MOON_REGISTER_COUNT = 'site_counts:moon_registered_count';
 | 
				
			||||
 | 
				
			||||
    // 订单的 key 统计
 | 
				
			||||
    const ORDER_COUNT = 'site_counts:order_count';
 | 
				
			||||
    // 成功支付的订单
 | 
				
			||||
    const PAY_ORDER_COUNT = 'site_counts:order_pay_count';
 | 
				
			||||
    // 退款订单量
 | 
				
			||||
    const REFUND_ORDER_COUNT = 'site_counts:refund_pay_count';
 | 
				
			||||
    // 销售金额
 | 
				
			||||
    const SALE_ORDER_COUNT = 'site_counts:sale_money_count';
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,12 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class UserSexEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    const MAN = 1;
 | 
				
			||||
    const WOMAN = 2;
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,15 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class UserSourceEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    // 默认上城创建
 | 
				
			||||
    const MOON = 1;
 | 
				
			||||
    const GITHUB = 2;
 | 
				
			||||
    const QQ = 3;
 | 
				
			||||
    const WEIBO = 4;
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,12 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Enums;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
use MyCLabs\Enum\Enum;
 | 
				
			||||
 | 
				
			||||
class UserStatusEnum extends Enum
 | 
				
			||||
{
 | 
				
			||||
    const ACTIVE = 1;
 | 
				
			||||
    const UN_ACTIVE = 0;
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,36 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Events;
 | 
				
			||||
 | 
				
			||||
use Illuminate\Broadcasting\Channel;
 | 
				
			||||
use Illuminate\Queue\SerializesModels;
 | 
				
			||||
use Illuminate\Broadcasting\PrivateChannel;
 | 
				
			||||
use Illuminate\Broadcasting\PresenceChannel;
 | 
				
			||||
use Illuminate\Foundation\Events\Dispatchable;
 | 
				
			||||
use Illuminate\Broadcasting\InteractsWithSockets;
 | 
				
			||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
 | 
				
			||||
 | 
				
			||||
class Event
 | 
				
			||||
{
 | 
				
			||||
    use Dispatchable, InteractsWithSockets, SerializesModels;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new event instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        //
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Get the channels the event should broadcast on.
 | 
				
			||||
     *
 | 
				
			||||
     * @return \Illuminate\Broadcasting\Channel|array
 | 
				
			||||
     */
 | 
				
			||||
    public function broadcastOn()
 | 
				
			||||
    {
 | 
				
			||||
        return new PrivateChannel('channel-name');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,10 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Exceptions;
 | 
				
			||||
 | 
				
			||||
use Exception;
 | 
				
			||||
 | 
				
			||||
class BadRequestException extends Exception
 | 
				
			||||
{
 | 
				
			||||
    //
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,62 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Exceptions;
 | 
				
			||||
 | 
				
			||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Validation\ValidationException;
 | 
				
			||||
use Throwable;
 | 
				
			||||
use Tymon\JWTAuth\Exceptions\JWTException;
 | 
				
			||||
use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
 | 
				
			||||
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
 | 
				
			||||
 | 
				
			||||
class Handler extends ExceptionHandler
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * The list of the inputs that are never flashed to the session on validation exceptions.
 | 
				
			||||
     *
 | 
				
			||||
     * @var array<int, string>
 | 
				
			||||
     */
 | 
				
			||||
    protected $dontFlash = [
 | 
				
			||||
        'current_password',
 | 
				
			||||
        'password',
 | 
				
			||||
        'password_confirmation',
 | 
				
			||||
    ];
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Register the exception handling callbacks for the application.
 | 
				
			||||
     */
 | 
				
			||||
    public function register(): void
 | 
				
			||||
    {
 | 
				
			||||
        $this->reportable(function (Throwable $e) {
 | 
				
			||||
            //
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        $this->renderable(function (\Exception $exception, Request $request) {
 | 
				
			||||
            if ($request->is('api*')) {
 | 
				
			||||
 | 
				
			||||
                if ($exception instanceof JWTException) {
 | 
				
			||||
 | 
				
			||||
                    $mapExceptions = [
 | 
				
			||||
                        TokenInvalidException::class => '无效的token',
 | 
				
			||||
                        TokenBlacklistedException::class => 'token 已被加入黑名单,请重新登录'
 | 
				
			||||
                    ];
 | 
				
			||||
 | 
				
			||||
                    $msg = $mapExceptions[get_class($exception)] ?? $exception->getMessage();
 | 
				
			||||
                    return responseJsonAsUnAuthorized($msg);
 | 
				
			||||
                }
 | 
				
			||||
                // 拦截表单验证错误抛出的异常
 | 
				
			||||
                elseif ($exception instanceof ValidationException) {
 | 
				
			||||
 | 
				
			||||
                    return responseJsonAsBadRequest($exception->validator->errors()->first());
 | 
				
			||||
                }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
                return responseJsonAsServerError($exception->getMessage());
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsServerError($exception->getMessage(), null);
 | 
				
			||||
        });
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,15 @@
 | 
				
			||||
<?php
 | 
				
			||||
/**
 | 
				
			||||
 * Created by PhpStorm.
 | 
				
			||||
 * User: David
 | 
				
			||||
 * Date: 2018/10/19
 | 
				
			||||
 * Time: 22:04
 | 
				
			||||
 */
 | 
				
			||||
 | 
				
			||||
namespace App\Exceptions;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
class OrderException extends \Exception
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,15 @@
 | 
				
			||||
<?php
 | 
				
			||||
/**
 | 
				
			||||
 * Created by PhpStorm.
 | 
				
			||||
 * User: David
 | 
				
			||||
 * Date: 2018/10/19
 | 
				
			||||
 * Time: 22:04
 | 
				
			||||
 */
 | 
				
			||||
 | 
				
			||||
namespace App\Exceptions;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
class UploadException extends \Exception
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,104 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Api\V1;
 | 
				
			||||
 | 
				
			||||
use App\Enums\UserSexEnum;
 | 
				
			||||
use App\Enums\UserStatusEnum;
 | 
				
			||||
use App\Exceptions\BadRequestException;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Http\Requests\LoginRequest;
 | 
				
			||||
use App\Http\Requests\RegisterUserRequest;
 | 
				
			||||
use App\Http\Resources\OwnResource;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\Hash;
 | 
				
			||||
 | 
				
			||||
class AuthController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * 登录的接口
 | 
				
			||||
     *
 | 
				
			||||
     * @param LoginRequest $request
 | 
				
			||||
     * @return \Illuminate\Http\JsonResponse
 | 
				
			||||
     */
 | 
				
			||||
    public function login(LoginRequest $request)
 | 
				
			||||
    {
 | 
				
			||||
        $username = $request->input('username');
 | 
				
			||||
        $password = $request->input('password');
 | 
				
			||||
 | 
				
			||||
        $user = User::query()
 | 
				
			||||
                    ->whereNotNull('name')
 | 
				
			||||
                    ->where('name', $username)
 | 
				
			||||
                    ->first();
 | 
				
			||||
 | 
				
			||||
        if (is_null($user)) {
 | 
				
			||||
            return responseJsonAsBadRequest('用户名或者密码错误');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if (! Hash::check($password, $user->getAuthPassword())) {
 | 
				
			||||
            return responseJsonAsBadRequest('用户名或者密码错误');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, '登录成功', $this->getToken($user));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 注销的接口
 | 
				
			||||
     *
 | 
				
			||||
     * @return \Illuminate\Http\JsonResponse
 | 
				
			||||
     */
 | 
				
			||||
    public function logout()
 | 
				
			||||
    {
 | 
				
			||||
        auth('api')->logout();
 | 
				
			||||
 | 
				
			||||
        return responseJsonAsDeleted('注销成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 注册的接口
 | 
				
			||||
     *
 | 
				
			||||
     * @param RegisterUserRequest $request
 | 
				
			||||
     * @return \Illuminate\Http\JsonResponse
 | 
				
			||||
     */
 | 
				
			||||
    public function register(RegisterUserRequest $request)
 | 
				
			||||
    {
 | 
				
			||||
        $username = $request->input('username');
 | 
				
			||||
        $password = $request->input('password');
 | 
				
			||||
 | 
				
			||||
        if (User::query()->where('name', $username)->exists()) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('用户名已经存在, 请换一个用户名');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $user = new User();
 | 
				
			||||
        $user->name = $username;
 | 
				
			||||
        $user->password = $password;
 | 
				
			||||
        $user->sex = UserSexEnum::MAN;
 | 
				
			||||
        $user->is_init_email = 1;
 | 
				
			||||
        // api 注册的用户默认激活
 | 
				
			||||
        $user->is_active = UserStatusEnum::ACTIVE;
 | 
				
			||||
        $user->save();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return responseJson(201, '注册成功', $this->getToken($user));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 拼接 token
 | 
				
			||||
     *
 | 
				
			||||
     * @param User $user
 | 
				
			||||
     * @return array
 | 
				
			||||
     */
 | 
				
			||||
    protected function getToken(User $user)
 | 
				
			||||
    {
 | 
				
			||||
        // 换取 token
 | 
				
			||||
        $prefix = 'Bearer';
 | 
				
			||||
        $token = auth('api')->login($user);
 | 
				
			||||
        $me = new OwnResource($user);
 | 
				
			||||
 | 
				
			||||
        return compact('prefix', 'token', 'me');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,73 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Api\V1;
 | 
				
			||||
 | 
				
			||||
use App\Http\Resources\CategoreResource;
 | 
				
			||||
use App\Http\Resources\ProductResource;
 | 
				
			||||
use App\Models\Category;
 | 
				
			||||
use App\Services\PageServe;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
 | 
				
			||||
class CategoryController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function index(PageServe $serve)
 | 
				
			||||
    {
 | 
				
			||||
        list($limit, $offset) = $serve->getPageParameters();
 | 
				
			||||
 | 
				
			||||
        $query = Category::query();
 | 
				
			||||
 | 
				
			||||
        if ($title = $serve->input('name')) {
 | 
				
			||||
 | 
				
			||||
            $query->where('title', 'like', "%{$title}%");
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $count = $query->count();
 | 
				
			||||
        $categories = $query->orderBy('order')->limit($limit)->offset($offset)->get();
 | 
				
			||||
        $categories = CategoreResource::collection($categories);
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, 'success', $categories, compact('count'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function getProducts(PageServe $serve, $category)
 | 
				
			||||
    {
 | 
				
			||||
        list($limit, $offset) = $serve->getPageParameters();
 | 
				
			||||
 | 
				
			||||
        // 排序的字段和排序的值
 | 
				
			||||
        $orderField = $serve->input('order_field');
 | 
				
			||||
        $orderValue = $serve->input('order_value');
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $category Category
 | 
				
			||||
         */
 | 
				
			||||
        $category = Category::query()->findOrFail($category);
 | 
				
			||||
 | 
				
			||||
        $query = $category->products();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        if ($name = $serve->input('name')) {
 | 
				
			||||
 | 
				
			||||
            $query->where('name', 'like', "%{$name}%");
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 获取排序的字段
 | 
				
			||||
        $allFields = ['created_at', 'sale_count', 'view_count'];
 | 
				
			||||
        $orderField = in_array($orderField, $allFields) ?
 | 
				
			||||
            $orderField :
 | 
				
			||||
            array_first($allFields);
 | 
				
			||||
        $orderValue = $orderValue === 'asc' ? 'asc' : 'desc';
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 获取数据
 | 
				
			||||
        $count = $query->count();
 | 
				
			||||
        $products = $query->orderBy($orderField, $orderValue)
 | 
				
			||||
                          ->limit($limit)
 | 
				
			||||
                          ->offset($offset)
 | 
				
			||||
                          ->get();
 | 
				
			||||
        $products = ProductResource::collection($products);
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, 'success', $products, compact('count'));
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,14 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Api\V1;
 | 
				
			||||
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
 | 
				
			||||
class HomeController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function ping()
 | 
				
			||||
    {
 | 
				
			||||
        return responseJsonAsUnAuthorized();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,41 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Api\V1;
 | 
				
			||||
 | 
				
			||||
use App\Http\Resources\OwnResource;
 | 
				
			||||
use App\Http\Resources\ScoreLogResource;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Services\PageServe;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
 | 
				
			||||
class OwnController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function me()
 | 
				
			||||
    {
 | 
				
			||||
        $me = auth()->user();
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, 'success', new OwnResource($me));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function scoreLogs(PageServe $serve)
 | 
				
			||||
    {
 | 
				
			||||
        list($limit, $offset) = $serve->getPageParameters();
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $me User
 | 
				
			||||
         */
 | 
				
			||||
        $me = auth()->user();
 | 
				
			||||
 | 
				
			||||
        $query = $me->scoreLogs();
 | 
				
			||||
 | 
				
			||||
        $count = $query->count();
 | 
				
			||||
        $scoreLogs = $me->scoreLogs()
 | 
				
			||||
                        ->latest()
 | 
				
			||||
                        ->offset($offset)
 | 
				
			||||
                        ->limit($limit)
 | 
				
			||||
                        ->get();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, 'success', ScoreLogResource::collection($scoreLogs), compact('count'));
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,25 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Api\V1;
 | 
				
			||||
 | 
				
			||||
use App\Http\Resources\ProductResource;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use Illuminate\Support\Facades\Cache;
 | 
				
			||||
 | 
				
			||||
class ProductController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function show($uuid)
 | 
				
			||||
    {
 | 
				
			||||
        $product = Product::query()->where('uuid', $uuid)->firstOrFail();
 | 
				
			||||
        $product->load('detail');
 | 
				
			||||
 | 
				
			||||
        // 直接使用缓存
 | 
				
			||||
        $today = Carbon::today()->toDateString();
 | 
				
			||||
        Cache::increment($product->getViewCountKey($today));
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, 'success', new ProductResource($product));
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,39 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Auth;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
 | 
				
			||||
use Illuminate\Support\Facades\Auth;
 | 
				
			||||
 | 
				
			||||
class ForgotPasswordController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    /*
 | 
				
			||||
    |--------------------------------------------------------------------------
 | 
				
			||||
    | Password Reset Controller
 | 
				
			||||
    |--------------------------------------------------------------------------
 | 
				
			||||
    |
 | 
				
			||||
    | This controller is responsible for handling password reset emails and
 | 
				
			||||
    | includes a trait which assists in sending these notifications from
 | 
				
			||||
    | your application to your users. Feel free to explore this trait.
 | 
				
			||||
    |
 | 
				
			||||
    */
 | 
				
			||||
 | 
				
			||||
    use SendsPasswordResetEmails;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new controller instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        $this->middleware('guest');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    protected function guard()
 | 
				
			||||
    {
 | 
				
			||||
        return auth()->guard();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,145 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Auth;
 | 
				
			||||
 | 
				
			||||
use App\Enums\UserSexEnum;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Mail\UserRegister;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Gregwar\Captcha\CaptchaBuilder;
 | 
				
			||||
use Illuminate\Auth\Events\Registered;
 | 
				
			||||
use Illuminate\Foundation\Auth\RegistersUsers;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\Log;
 | 
				
			||||
use Illuminate\Support\Facades\Mail;
 | 
				
			||||
use Illuminate\Support\Facades\Validator;
 | 
				
			||||
use Illuminate\Validation\Rule;
 | 
				
			||||
 | 
				
			||||
class RegisterController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    /*
 | 
				
			||||
    |--------------------------------------------------------------------------
 | 
				
			||||
    | Register Controller
 | 
				
			||||
    |--------------------------------------------------------------------------
 | 
				
			||||
    |
 | 
				
			||||
    | This controller handles the registration of new users as well as their
 | 
				
			||||
    | validation and creation. By default this controller uses a trait to
 | 
				
			||||
    | provide this functionality without requiring any additional code.
 | 
				
			||||
    |
 | 
				
			||||
    */
 | 
				
			||||
 | 
				
			||||
    use RegistersUsers;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Where to redirect users after registration.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $redirectTo = '/home';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new controller instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        $this->middleware('guest');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 核心注册方法
 | 
				
			||||
     *
 | 
				
			||||
     * @param Request $request
 | 
				
			||||
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
 | 
				
			||||
     * @throws \Illuminate\Validation\ValidationException
 | 
				
			||||
     */
 | 
				
			||||
    public function register(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        $this->validator($request->all())->validate();
 | 
				
			||||
 | 
				
			||||
        if (strtolower($request->input('captcha')) != strtolower(session('captcha'))) {
 | 
				
			||||
 | 
				
			||||
            return redirect('/register')->withErrors(['captcha' => '验证码不正确']);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        event(new Registered($user = $this->create($request->all())));
 | 
				
			||||
 | 
				
			||||
        $this->registered($request, $user);
 | 
				
			||||
        return redirect('/login')->with('status', '注册成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * registered event (send email)
 | 
				
			||||
     * @param Request $request
 | 
				
			||||
     * @param $user
 | 
				
			||||
     */
 | 
				
			||||
    protected function registered(Request $request, $user)
 | 
				
			||||
    {
 | 
				
			||||
        Mail::to($user->email)
 | 
				
			||||
            ->queue(new UserRegister($user));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    protected function validator(array $data)
 | 
				
			||||
    {
 | 
				
			||||
        return Validator::make($data, [
 | 
				
			||||
            'name' => 'required|string|max:50|unique:users',
 | 
				
			||||
            'email' => 'required|string|email|max:50|unique:users',
 | 
				
			||||
            'password' => 'required|string|min:5|confirmed',
 | 
				
			||||
            'sex' => ['required', Rule::in([UserSexEnum::MAN, UserSexEnum::WOMAN])],
 | 
				
			||||
            'captcha' => 'required',
 | 
				
			||||
        ], [
 | 
				
			||||
            'name.required' => '用户名不能为空',
 | 
				
			||||
            'name.max' => '用户名不能超过50个字符',
 | 
				
			||||
            'name.unique' => '用户名已经被占用',
 | 
				
			||||
            'email.unique' => '邮箱已经被占用',
 | 
				
			||||
            'email.required' => '邮箱不能为空',
 | 
				
			||||
            'email.email' => '邮箱格式不正确',
 | 
				
			||||
            'password.min' => '密码最少六位数',
 | 
				
			||||
            'password.required' => '密码不能为空',
 | 
				
			||||
            'password.confirmed' => '两次密码不一致',
 | 
				
			||||
            'captcha.required' => '验证码不能为空',
 | 
				
			||||
            'sex.in' => '性别错误',
 | 
				
			||||
        ]);
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    protected function create(array $data)
 | 
				
			||||
    {
 | 
				
			||||
        // email_active,
 | 
				
			||||
        return  User::query()->create([
 | 
				
			||||
            'name' => $data['name'],
 | 
				
			||||
            'email' => $data['email'],
 | 
				
			||||
            'sex' => $data['sex'],
 | 
				
			||||
            'password' => bcrypt($data['password']),
 | 
				
			||||
            'active_token' => str_random(60),
 | 
				
			||||
            'is_active' => 1,
 | 
				
			||||
        ]);
 | 
				
			||||
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    protected function redirectTo()
 | 
				
			||||
    {
 | 
				
			||||
        return 'register';
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * @return string
 | 
				
			||||
     */
 | 
				
			||||
    public function captcha()
 | 
				
			||||
    {
 | 
				
			||||
        $builder = (new CaptchaBuilder(4))->build(150, 46);
 | 
				
			||||
 | 
				
			||||
        session()->put('captcha', $builder->getPhrase());
 | 
				
			||||
 | 
				
			||||
        return $builder->get();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    protected function guard()
 | 
				
			||||
    {
 | 
				
			||||
        return auth()->guard();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,79 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Auth;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use Illuminate\Auth\Events\PasswordReset;
 | 
				
			||||
use Illuminate\Foundation\Auth\ResetsPasswords;
 | 
				
			||||
use Illuminate\Support\Facades\Hash;
 | 
				
			||||
use Illuminate\Support\Str;
 | 
				
			||||
 | 
				
			||||
class ResetPasswordController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    /*
 | 
				
			||||
    |--------------------------------------------------------------------------
 | 
				
			||||
    | Password Reset Controller
 | 
				
			||||
    |--------------------------------------------------------------------------
 | 
				
			||||
    |
 | 
				
			||||
    | This controller is responsible for handling password reset requests
 | 
				
			||||
    | and uses a simple trait to include this behavior. You're free to
 | 
				
			||||
    | explore this trait and override any methods you wish to tweak.
 | 
				
			||||
    |
 | 
				
			||||
    */
 | 
				
			||||
 | 
				
			||||
    use ResetsPasswords;
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Where to redirect users after resetting their password.
 | 
				
			||||
     *
 | 
				
			||||
     * @var string
 | 
				
			||||
     */
 | 
				
			||||
    protected $redirectTo = '/home';
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * Create a new controller instance.
 | 
				
			||||
     *
 | 
				
			||||
     * @return void
 | 
				
			||||
     */
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        $this->middleware('guest');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function validationErrorMessages()
 | 
				
			||||
    {
 | 
				
			||||
        return [
 | 
				
			||||
            'token.required' => '重置密码的token不是对应这个邮箱',
 | 
				
			||||
            'email.required' => '邮件地址不正确',
 | 
				
			||||
            'email.email' => '邮件地址不正确',
 | 
				
			||||
            'password.required' => '密码不能为空',
 | 
				
			||||
            'password.confirmed' => '两次密码不一致',
 | 
				
			||||
            'password.min' => '密码不能少于六位数',
 | 
				
			||||
        ];
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * rewrite Illuminate\Foundation\Auth\ResetsPasswords::resetPassword not login
 | 
				
			||||
     * @param $user
 | 
				
			||||
     * @param $password
 | 
				
			||||
     */
 | 
				
			||||
    public function resetPassword($user, $password)
 | 
				
			||||
    {
 | 
				
			||||
        $user->password = Hash::make($password);
 | 
				
			||||
        $user->setRememberToken(Str::random(60));
 | 
				
			||||
        $user->save();
 | 
				
			||||
 | 
				
			||||
        // event(new PasswordReset($user));
 | 
				
			||||
        // $this->guard()->login($user);
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function redirectTo()
 | 
				
			||||
    {
 | 
				
			||||
        return 'password/reset';
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    protected function guard()
 | 
				
			||||
    {
 | 
				
			||||
        return auth()->guard();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,39 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\Auth;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Mail\UserRegister;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Illuminate\Support\Facades\Mail;
 | 
				
			||||
 | 
				
			||||
class UserController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function activeAccount($token)
 | 
				
			||||
    {
 | 
				
			||||
        if ($user = User::query()->where('active_token', $token)->first()) {
 | 
				
			||||
            $user->is_active = 1;
 | 
				
			||||
            // 重新生成激活token
 | 
				
			||||
            $user->active_token = str_random(60);
 | 
				
			||||
            $user->save();
 | 
				
			||||
 | 
				
			||||
            return view('hint.success', ['status' => "{$user->name} 账户激活成功!", 'url' => url('login')]);
 | 
				
			||||
        } else {
 | 
				
			||||
            return view('hint.error', ['status' => '无效的token']);
 | 
				
			||||
        }
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function sendActiveMail($id)
 | 
				
			||||
    {
 | 
				
			||||
        if ($user = User::query()->find($id)) {
 | 
				
			||||
            //  again send active link, join queue
 | 
				
			||||
            Mail::to($user->email)
 | 
				
			||||
                ->queue(new UserRegister($user));
 | 
				
			||||
 | 
				
			||||
            return view('hint.success', ['status' => '发送邮件成功', 'url' => route('login')]);
 | 
				
			||||
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return view('hint.error', ['status' => '用户名或者密码错误']);
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,105 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\Car;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class CarController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function __construct()
 | 
				
			||||
    {
 | 
				
			||||
        $this->middleware('user.auth')->only('store', 'destroy');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 购物车列表
 | 
				
			||||
     *
 | 
				
			||||
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 | 
				
			||||
     */
 | 
				
			||||
    public function index()
 | 
				
			||||
    {
 | 
				
			||||
        $cars = collect();
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        if ($user = \auth()->user()) {
 | 
				
			||||
            // 直接获取当前登录用户的购物车
 | 
				
			||||
            $cars = $user->cars()->with('product')->get();
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return view('cars.index', compact('cars'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 添加购物车
 | 
				
			||||
     * @param Request $request
 | 
				
			||||
     * @return array
 | 
				
			||||
     */
 | 
				
			||||
    public function store(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $car Car
 | 
				
			||||
         * @var $product Product
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $product = Product::query()->where('uuid', $request->input('product_id'))->firstOrFail();
 | 
				
			||||
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
        $car = $user->cars()->firstOrNew([
 | 
				
			||||
            'user_id' => \auth()->id(),
 | 
				
			||||
            'product_id' => $product->id
 | 
				
			||||
        ]);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 如果是同步,则只是赋值,如果是添加购物车则是添加
 | 
				
			||||
        $change = 0;
 | 
				
			||||
        $number = $request->input('number', 1);
 | 
				
			||||
 | 
				
			||||
        if ($request->input('action') == 'sync') {
 | 
				
			||||
 | 
				
			||||
            $change = $number - $car->number;
 | 
				
			||||
            $car->number = $number;
 | 
				
			||||
        } else {
 | 
				
			||||
 | 
				
			||||
            $car->number += $number;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($car->number > $product->count) {
 | 
				
			||||
 | 
				
			||||
            return responseJson(403, '库存不足');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $car->save();
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, '加入购物车成功', compact('change'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * @param $id
 | 
				
			||||
     * @return array
 | 
				
			||||
     */
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        try {
 | 
				
			||||
            /**
 | 
				
			||||
             * @var $user User
 | 
				
			||||
             */
 | 
				
			||||
            $user = auth()->user();
 | 
				
			||||
            $car = $user->cars()->whereKey($id)->firstOrFail();
 | 
				
			||||
            $car->delete();
 | 
				
			||||
 | 
				
			||||
        } catch (\Exception $e) {
 | 
				
			||||
 | 
				
			||||
            dd($e);
 | 
				
			||||
            return responseJson(500, '服务器异常,请稍后再试');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, '删除成功');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,40 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\Category;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
class CategoryController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    /**
 | 
				
			||||
     * 前台分类列表
 | 
				
			||||
     *
 | 
				
			||||
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 | 
				
			||||
     */
 | 
				
			||||
    public function index()
 | 
				
			||||
    {
 | 
				
			||||
        $categories = Category::query()->latest()->paginate(30);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return view('categories.index', compact('categories'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 分类详情
 | 
				
			||||
     *
 | 
				
			||||
     * @param Request  $request
 | 
				
			||||
     * @param Category $category
 | 
				
			||||
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 | 
				
			||||
     */
 | 
				
			||||
    public function show(Request $request, Category $category)
 | 
				
			||||
    {
 | 
				
			||||
        $orderBy = $request->input('orderBy', 'created_at');
 | 
				
			||||
        $categoryProducts = $category->products()->withCount('users')->orderBy($orderBy, 'desc')->paginate(10);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return view('categories.show', compact('category', 'categoryProducts'));
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,13 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers;
 | 
				
			||||
 | 
				
			||||
use Illuminate\Foundation\Bus\DispatchesJobs;
 | 
				
			||||
use Illuminate\Routing\Controller as BaseController;
 | 
				
			||||
use Illuminate\Foundation\Validation\ValidatesRequests;
 | 
				
			||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
 | 
				
			||||
 | 
				
			||||
class Controller extends BaseController
 | 
				
			||||
{
 | 
				
			||||
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,92 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\CouponTemplate;
 | 
				
			||||
use App\Models\ScoreLog;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Models\UserHasCoupon;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\DB;
 | 
				
			||||
 | 
				
			||||
class CouponController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function store(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        $templateId = $request->input('template_id');
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
        if (is_null($user)) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsUnAuthorized('请先登录再领取优惠券');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $template = CouponTemplate::query()->find($templateId);
 | 
				
			||||
        if (is_null($template)) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('无效的优惠券');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 判断优惠券是否过期
 | 
				
			||||
        $today = Carbon::today();
 | 
				
			||||
        $endDate = Carbon::make($template->end_date);
 | 
				
			||||
        if ($today->gt($endDate)) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('优惠券已过期');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($user->score_now < $template->score) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest("积分不足{$template->score},请先去获取积分");
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 这里我会只让每个用户只能领取一张优惠券
 | 
				
			||||
        if ($user->coupons()->where('template_id', $templateId)->exists()) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('你已经领取过优惠券了');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        DB::beginTransaction();
 | 
				
			||||
 | 
				
			||||
        try {
 | 
				
			||||
 | 
				
			||||
            // 用户减少积分
 | 
				
			||||
            if ($template->score > 0) {
 | 
				
			||||
 | 
				
			||||
                $user->score_now -= $template->score;
 | 
				
			||||
                $user->save();
 | 
				
			||||
                // 生成积分日志
 | 
				
			||||
                $log = new ScoreLog();
 | 
				
			||||
                $log->user_id = $user->getKey();
 | 
				
			||||
                $log->description = "领取优惠券";
 | 
				
			||||
                $log->score = -1 * $template->score;
 | 
				
			||||
                $log->save();
 | 
				
			||||
            }
 | 
				
			||||
 | 
				
			||||
            // 开始领取优惠券
 | 
				
			||||
            $coupon = new UserHasCoupon();
 | 
				
			||||
            $coupon->template_id = $template->getKey();
 | 
				
			||||
            $coupon->user_id = $user->getKey();
 | 
				
			||||
 | 
				
			||||
            $coupon->title = $template->title;
 | 
				
			||||
            $coupon->amount = $template->amount;
 | 
				
			||||
            $coupon->full_amount = $template->full_amount;
 | 
				
			||||
            $coupon->start_date = $template->start_date;
 | 
				
			||||
            $coupon->end_date = $template->end_date;
 | 
				
			||||
            $coupon->save();
 | 
				
			||||
 | 
				
			||||
        } catch (\Exception $e) {
 | 
				
			||||
 | 
				
			||||
            DB::rollBack();
 | 
				
			||||
            return responseJsonAsServerError('服务器异常,请稍后再试');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        DB::commit();
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, '领取成功');
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,28 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\CouponTemplate;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
 | 
				
			||||
class CouponTemplateController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function index()
 | 
				
			||||
    {
 | 
				
			||||
        $today = Carbon::today()->toDateString();
 | 
				
			||||
 | 
				
			||||
        // 只查询未过期的
 | 
				
			||||
        // 标记已经领取过的
 | 
				
			||||
        $templates = CouponTemplate::query()
 | 
				
			||||
            ->withCount(['coupons' => function ($b) {
 | 
				
			||||
 | 
				
			||||
                $b->where('user_id', auth()->id());
 | 
				
			||||
            }])
 | 
				
			||||
            ->where('end_date', '>=', $today)
 | 
				
			||||
            ->get();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return view('coupons.templates', compact('templates'));
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,147 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers;
 | 
				
			||||
 | 
				
			||||
use App\Models\Node;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\ProductPinYin;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Services\ScoreLogServe;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Pagination\LengthAwarePaginator;
 | 
				
			||||
use Illuminate\Support\Collection;
 | 
				
			||||
use Illuminate\Support\Facades\Auth;
 | 
				
			||||
use Illuminate\Support\Facades\Cache;
 | 
				
			||||
 | 
				
			||||
class ProductController extends Controller
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 商品列表
 | 
				
			||||
     *
 | 
				
			||||
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 | 
				
			||||
     */
 | 
				
			||||
    public function index()
 | 
				
			||||
    {
 | 
				
			||||
        // 随机查出一些商品展示
 | 
				
			||||
        $products = Product::query()->inRandomOrder()->take(9)->get(['uuid', 'name'])->split(3);
 | 
				
			||||
        $pinyins = ProductPinYin::query()->orderBy('pinyin')->pluck('pinyin');
 | 
				
			||||
 | 
				
			||||
        return view('products.index', compact('products', 'pinyins'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * ajax 通过商品首字母查询商品
 | 
				
			||||
     * @param $pinyin
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    public function getProductsByPinyin($pinyin)
 | 
				
			||||
    {
 | 
				
			||||
        $products = Product::query()->where('first_pinyin', $pinyin)->get(['id', 'name'])->split(3);
 | 
				
			||||
 | 
				
			||||
        return $products;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 商品搜索
 | 
				
			||||
     *
 | 
				
			||||
     * @param Request $request
 | 
				
			||||
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 | 
				
			||||
     */
 | 
				
			||||
    public function search(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        $keyword = $request->input('keyword', '');
 | 
				
			||||
        $page = abs((int)$request->get('page', 1));
 | 
				
			||||
        $limit = (int)$request->get('limit', 20);
 | 
				
			||||
        $offset = (int) ($page - 1) * $limit;
 | 
				
			||||
        
 | 
				
			||||
        // 全文索引
 | 
				
			||||
        try {
 | 
				
			||||
    
 | 
				
			||||
            $parameters = [
 | 
				
			||||
                'multi_match' => [
 | 
				
			||||
                    'query' => $keyword,
 | 
				
			||||
                    'fields' => ['title', 'body'],
 | 
				
			||||
                ]
 | 
				
			||||
            ];
 | 
				
			||||
            
 | 
				
			||||
            $count = Product::searchCount($parameters);
 | 
				
			||||
            $searchCount = $count['count'] ?? 0;
 | 
				
			||||
            $searchResult = Product::search($parameters, $limit, $offset);
 | 
				
			||||
            $filterIds = Collection::make($searchResult['hits']['hits'] ?? [])->pluck('_source.id');
 | 
				
			||||
            $models = Product::query()->findMany($filterIds);
 | 
				
			||||
    
 | 
				
			||||
            $products = new LengthAwarePaginator($models, $searchCount, $limit, $page);
 | 
				
			||||
            
 | 
				
			||||
        } catch (\Exception $e) {
 | 
				
			||||
    
 | 
				
			||||
            $products = Product::query()->withCount('users')->where('name', 'like', "%{$keyword}%")->paginate($limit);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return view('products.search', compact('products'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 单个商品显示
 | 
				
			||||
     *
 | 
				
			||||
     * @param                 $uuid
 | 
				
			||||
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 | 
				
			||||
     */
 | 
				
			||||
    public function show($uuid)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User|null
 | 
				
			||||
         */
 | 
				
			||||
        $product = Product::query()->where('uuid', $uuid)->firstOrFail();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        if (! $product->today_has_view) {
 | 
				
			||||
            $product->today_has_view = true;
 | 
				
			||||
            $product->save();
 | 
				
			||||
        }
 | 
				
			||||
        // 直接使用缓存
 | 
				
			||||
        $today = Carbon::today()->toDateString();
 | 
				
			||||
        Cache::increment($product->getViewCountKey($today));
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 商品浏览次数 + 1
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
 | 
				
			||||
        // 同类商品推荐
 | 
				
			||||
        $recommendProducts = Product::query()
 | 
				
			||||
                                    ->where('category_id', $product->category_id)
 | 
				
			||||
                                    ->take(5)
 | 
				
			||||
                                    ->get();
 | 
				
			||||
 | 
				
			||||
        // 加载出详情,收藏的人数, 评论
 | 
				
			||||
        $product->load([
 | 
				
			||||
            'detail',
 | 
				
			||||
            'users',
 | 
				
			||||
            'comments' => function ($query) {
 | 
				
			||||
                $query->latest();
 | 
				
			||||
            },
 | 
				
			||||
            'comments.user'
 | 
				
			||||
        ]);
 | 
				
			||||
        $product->userIsLike = $product->users()->where('id', auth()->id())->exists();
 | 
				
			||||
 | 
				
			||||
        // 如果登录返回所有地址列表,如果没有,则返回一个空集合
 | 
				
			||||
        if ($user) {
 | 
				
			||||
 | 
				
			||||
            // 浏览商品增加积分
 | 
				
			||||
            (new ScoreLogServe)->visitedProductAddScore($user, $product);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return view('products.show', compact('product', 'recommendProducts'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * @return \Illuminate\Contracts\Auth\StatefulGuard
 | 
				
			||||
     */
 | 
				
			||||
    protected function guard()
 | 
				
			||||
    {
 | 
				
			||||
        return auth()->guard();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,121 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\User;
 | 
				
			||||
 | 
				
			||||
use App\Http\Requests\AddressRequest;
 | 
				
			||||
use App\Models\Address;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Auth;
 | 
				
			||||
use DB;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
 | 
				
			||||
class AddressController extends Controller
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
    public function index()
 | 
				
			||||
    {
 | 
				
			||||
        $addresses = auth()->user()->addresses;
 | 
				
			||||
 | 
				
			||||
        // Provincial and municipal regions
 | 
				
			||||
        $provinces = DB::table('provinces')->get();
 | 
				
			||||
        $cities = DB::table('cities')->where('province_id', $provinces->first()->id)->get();
 | 
				
			||||
 | 
				
			||||
        return view('user.addresses.index', compact('addresses', 'provinces', 'cities'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function store(AddressRequest $request)
 | 
				
			||||
    {
 | 
				
			||||
        $addressesData = $this->getFormatRequest($request);
 | 
				
			||||
 | 
				
			||||
        //
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
        $user->addresses()->create($addressesData);
 | 
				
			||||
 | 
				
			||||
        return back()->with('status', '创建成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function show(Address $address)
 | 
				
			||||
    {
 | 
				
			||||
        return $address;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function edit(Address $address)
 | 
				
			||||
    {
 | 
				
			||||
        if (auth()->id() != $address->user_id) {
 | 
				
			||||
 | 
				
			||||
            abort(403, '非法操作');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $addresses = auth()->user()->addresses;
 | 
				
			||||
        // Provincial and municipal regions
 | 
				
			||||
        $provinces = DB::table('provinces')->get();
 | 
				
			||||
        $cities = DB::table('cities')->where('province_id', $address->province_id)->get();
 | 
				
			||||
 | 
				
			||||
        return view('user.addresses.edit', compact('addresses', 'address', 'provinces', 'cities'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function update(AddressRequest $request, Address $address)
 | 
				
			||||
    {
 | 
				
			||||
        if (auth()->id() != $address->user_id) {
 | 
				
			||||
 | 
				
			||||
            abort(403, '非法操作');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $addressesData = $this->getFormatRequest($request);
 | 
				
			||||
 | 
				
			||||
        $address->update($addressesData);
 | 
				
			||||
 | 
				
			||||
        return back()->with('status', '修改成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function destroy(Address $address)
 | 
				
			||||
    {
 | 
				
			||||
        if (auth()->id() != $address->user_id) {
 | 
				
			||||
 | 
				
			||||
            return responseJson(400, '非法操作');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $address->delete();
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, '删除成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function setDefaultAddress(Address $address)
 | 
				
			||||
    {
 | 
				
			||||
        if (auth()->id() != $address->user_id) {
 | 
				
			||||
 | 
				
			||||
            return responseJson(400, '非法操作');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        Address::query()->where('user_id', $address->user_id)->update(['is_default' => 0]);
 | 
				
			||||
        $address->is_default = 1;
 | 
				
			||||
 | 
				
			||||
        if ($address->save()) {
 | 
				
			||||
 | 
				
			||||
            return responseJson(0, '设置成功');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return responseJson(400, '请稍后再试!');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    protected function getFormatRequest(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        return $request->only(['name', 'phone', 'province_id', 'city_id','detail_address']);
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function getCities(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        return DB::table('cities')->where('province_id', $request->input('province_id'))->get();
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,65 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\User;
 | 
				
			||||
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Auth;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
 | 
				
			||||
class LikesController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    protected $response = [
 | 
				
			||||
        'code' => 1,
 | 
				
			||||
        'msg' => '服务器异常,请稍后再试',
 | 
				
			||||
    ];
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function index()
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
 | 
				
			||||
        $likesProducts = $user->products()
 | 
				
			||||
                              ->where('user_id', auth()->id())
 | 
				
			||||
                              ->withCount('users')
 | 
				
			||||
                              ->latest()
 | 
				
			||||
                              ->paginate(10);
 | 
				
			||||
 | 
				
			||||
        return view('user.products.likes', compact('likesProducts'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function toggle($uuid)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $product Product
 | 
				
			||||
         */
 | 
				
			||||
        $product = Product::query()
 | 
				
			||||
                          ->where('uuid', $uuid)
 | 
				
			||||
                          ->firstOrFail();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $user = auth()->id();
 | 
				
			||||
 | 
				
			||||
        if ($product->users()->where('user_id', $user)->exists()) {
 | 
				
			||||
 | 
				
			||||
            $product->users()->detach($user);
 | 
				
			||||
 | 
				
			||||
            return response()->json([
 | 
				
			||||
                'code' => 200,
 | 
				
			||||
                'msg' => '欢迎下次收藏'
 | 
				
			||||
            ]);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $product->users()->attach($user);
 | 
				
			||||
 | 
				
			||||
        return response()->json([
 | 
				
			||||
            'code' => 201,
 | 
				
			||||
            'msg' => '收藏成功'
 | 
				
			||||
        ]);
 | 
				
			||||
}
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,149 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\User;
 | 
				
			||||
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Notifications\CouponCodeNotification;
 | 
				
			||||
use App\Services\NotificationServe;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Notifications\DatabaseNotification;
 | 
				
			||||
 | 
				
			||||
class NotificationController extends Controller
 | 
				
			||||
{
 | 
				
			||||
    public function index(Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        switch ($request->input('tab', 1)) {
 | 
				
			||||
 | 
				
			||||
            case 2:
 | 
				
			||||
                $query = $user->notifications();
 | 
				
			||||
                break;
 | 
				
			||||
            case 3:
 | 
				
			||||
                $query = $user->readNotifications();
 | 
				
			||||
                break;
 | 
				
			||||
            case 1:
 | 
				
			||||
            default:
 | 
				
			||||
                $query = $user->unreadNotifications();
 | 
				
			||||
                break;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $notifications = $query->paginate();
 | 
				
			||||
 | 
				
			||||
        foreach ($notifications as $notification) {
 | 
				
			||||
 | 
				
			||||
            $notification->title = NotificationServe::getTitle($notification);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $unreadCount = $user->unreadNotifications()->count();
 | 
				
			||||
        $readCount = $user->readNotifications()->count();
 | 
				
			||||
 | 
				
			||||
        return view('user.notifications.index', compact('notifications', 'unreadCount', 'readCount'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function read($id)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $notification DatabaseNotification
 | 
				
			||||
         */
 | 
				
			||||
        $notification = $user->notifications()->find($id);
 | 
				
			||||
        if (is_null($notification)) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('无效的通知');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $notification->markAsRead();
 | 
				
			||||
 | 
				
			||||
        return responseJson();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function readAll()
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $count = $user->unreadNotifications()->update(['read_at' => Carbon::now()]);
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, "本次已读{$count}条消息");
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    public function show($id)
 | 
				
			||||
    {
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $notification DatabaseNotification
 | 
				
			||||
         */
 | 
				
			||||
        $notification = $user->notifications()->find($id);
 | 
				
			||||
        if (is_null($notification)) {
 | 
				
			||||
 | 
				
			||||
            return abort(403, '无效的通知');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 查看是否有上一条下一条
 | 
				
			||||
        $last = $user->notifications()->where('created_at', '<', $notification->created_at)->first();
 | 
				
			||||
        $next = $user->notifications()->where('created_at', '>', $notification->created_at)->first();
 | 
				
			||||
        // 标记为已读
 | 
				
			||||
        $notification->markAsRead();
 | 
				
			||||
 | 
				
			||||
        $view = NotificationServe::getView($notification);
 | 
				
			||||
        if (! view()->exists($view)) {
 | 
				
			||||
 | 
				
			||||
            abort(404, '未知的的消息');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $notification->title = NotificationServe::getTitle($notification);
 | 
				
			||||
        $data = $notification->data;
 | 
				
			||||
 | 
				
			||||
        return view('user.notifications.show', compact('last', 'next', 'notification', 'view', 'data'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function getUnreadCount()
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $notification DatabaseNotification
 | 
				
			||||
         */
 | 
				
			||||
        $count = $user->unreadNotifications()->count();
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $title = '';
 | 
				
			||||
        $content = '';
 | 
				
			||||
        $id = null;
 | 
				
			||||
        if ($count > 0) {
 | 
				
			||||
 | 
				
			||||
            $notification = $user->unreadNotifications()->first();
 | 
				
			||||
 | 
				
			||||
            // 前端弹窗内容和标题相反显示,所以变量名会有点怪
 | 
				
			||||
            $id = $notification->id;
 | 
				
			||||
            $title = NotificationServe::getContent($notification);
 | 
				
			||||
            $content = NotificationServe::getTitle($notification);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, 'success', compact('count', 'title', 'content', 'id'));
 | 
				
			||||
    }
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,326 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\User;
 | 
				
			||||
 | 
				
			||||
use App\Admin\Transforms\OrderShipStatusTransform;
 | 
				
			||||
use App\Admin\Transforms\OrderStatusTransform;
 | 
				
			||||
use App\Enums\OrderShipStatusEnum;
 | 
				
			||||
use App\Enums\OrderStatusEnum;
 | 
				
			||||
use App\Enums\ScoreRuleIndexEnum;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Models\Address;
 | 
				
			||||
use App\Models\Comment;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
use App\Models\OrderDetail;
 | 
				
			||||
use App\Models\ScoreRule;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use App\Services\OrderStatusButtonServe;
 | 
				
			||||
use App\Services\ScoreLogServe;
 | 
				
			||||
use Illuminate\Http\Request;
 | 
				
			||||
use Illuminate\Support\Facades\DB;
 | 
				
			||||
use Yansongda\Pay\Pay;
 | 
				
			||||
 | 
				
			||||
class OrderController extends Controller
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
    public function index()
 | 
				
			||||
    {
 | 
				
			||||
        // 获取积分比例
 | 
				
			||||
        $scoreRatio = $this->getScoreRatio();
 | 
				
			||||
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $user User
 | 
				
			||||
         */
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
        $query = $user->orders();
 | 
				
			||||
 | 
				
			||||
        switch (request('tab', 0)) {
 | 
				
			||||
 | 
				
			||||
            case 0:
 | 
				
			||||
                break;
 | 
				
			||||
            case 1:
 | 
				
			||||
                // 待付款
 | 
				
			||||
                $query->where('status', OrderStatusEnum::UN_PAY);
 | 
				
			||||
                break;
 | 
				
			||||
            case 2:
 | 
				
			||||
                // 未发货
 | 
				
			||||
                $query->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::PENDING);
 | 
				
			||||
                break;
 | 
				
			||||
            case 3:
 | 
				
			||||
                // 待收货
 | 
				
			||||
                $query->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::DELIVERED);
 | 
				
			||||
                break;
 | 
				
			||||
            case 4:
 | 
				
			||||
                // 待评价
 | 
				
			||||
                $query->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::RECEIVED);
 | 
				
			||||
                break;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $orders = $query->latest()
 | 
				
			||||
                        ->with('details', 'details.product')
 | 
				
			||||
                        ->get()
 | 
				
			||||
                        ->map(
 | 
				
			||||
                            function (Order $order) use ($scoreRatio) {
 | 
				
			||||
 | 
				
			||||
                                // 可以或得到的积分
 | 
				
			||||
                                $order->score = ceil($order->amount*$scoreRatio);
 | 
				
			||||
 | 
				
			||||
                                // 完成按钮必须是已经支付和确认收货
 | 
				
			||||
                                $order->status_text = OrderStatusTransform::trans($order->status);
 | 
				
			||||
                                // 如果订单是付款了则显示发货状态
 | 
				
			||||
                                if ($order->status == OrderStatusEnum::PAID) {
 | 
				
			||||
 | 
				
			||||
                                    // 如果发货了,则显示发货信息
 | 
				
			||||
                                    $order->status_text = OrderShipStatusTransform::trans($order->ship_status);
 | 
				
			||||
                                }
 | 
				
			||||
 | 
				
			||||
                                $buttonServe = new OrderStatusButtonServe($order);
 | 
				
			||||
                                switch ($order->status) {
 | 
				
			||||
                                    // 未支付的
 | 
				
			||||
                                    case OrderStatusEnum::UN_PAY:
 | 
				
			||||
 | 
				
			||||
                                        $buttonServe->payButton()->cancelOrderButton();
 | 
				
			||||
                                        break;
 | 
				
			||||
                                    case OrderStatusEnum::PAID:
 | 
				
			||||
                                        // 已经确认收获了
 | 
				
			||||
                                        if ($order->ship_status == OrderShipStatusEnum::RECEIVED) {
 | 
				
			||||
 | 
				
			||||
                                            $buttonServe->completeButton();
 | 
				
			||||
                                        } elseif ($order->ship_status == OrderShipStatusEnum::DELIVERED) {
 | 
				
			||||
 | 
				
			||||
                                            $buttonServe->shipButton();
 | 
				
			||||
                                        } else {
 | 
				
			||||
 | 
				
			||||
                                            $buttonServe->refundButton();
 | 
				
			||||
                                        }
 | 
				
			||||
                                        break;
 | 
				
			||||
 | 
				
			||||
                                    // 手动取消的订单
 | 
				
			||||
                                    // 已经完成的订单
 | 
				
			||||
                                    // 超时取消的订单
 | 
				
			||||
                                    case OrderStatusEnum::UN_PAY_CANCEL:
 | 
				
			||||
                                    case OrderStatusEnum::COMPLETED:
 | 
				
			||||
                                    case OrderStatusEnum::TIMEOUT_CANCEL:
 | 
				
			||||
                                        $buttonServe->replyBuyButton()->deleteButton();
 | 
				
			||||
                                        break;
 | 
				
			||||
                                }
 | 
				
			||||
 | 
				
			||||
                                $order->buttons = $buttonServe->getButtons();
 | 
				
			||||
 | 
				
			||||
                                return $order;
 | 
				
			||||
                            }
 | 
				
			||||
                        );
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        // 查询订单总量
 | 
				
			||||
        $unPayCount = $user->orders()->where('status', OrderStatusEnum::UN_PAY)->count();
 | 
				
			||||
        $shipPendingCount = $user->orders()->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::PENDING)->count();
 | 
				
			||||
        $shipDeliveredCount = $user->orders()->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::DELIVERED)->count();
 | 
				
			||||
        $shipReceivedCount = $user->orders()->where('status', OrderStatusEnum::PAID)->where('ship_status', OrderShipStatusEnum::RECEIVED)->count();
 | 
				
			||||
        $ordersCount = $user->orders()->count();
 | 
				
			||||
 | 
				
			||||
        return view('user.orders.index', compact('orders', 'unPayCount', 'shipPendingCount', 'shipDeliveredCount', 'shipReceivedCount', 'ordersCount'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function show(Order $order)
 | 
				
			||||
    {
 | 
				
			||||
        if ($order->isNotUser(auth()->id())) {
 | 
				
			||||
            abort(403, '你没有权限');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $order->ship_send = $order->ship_status == OrderShipStatusEnum::DELIVERED;
 | 
				
			||||
        $order->confirm_ship = $order->ship_status == OrderShipStatusEnum::RECEIVED;
 | 
				
			||||
 | 
				
			||||
        if ($order->confirm_ship) {
 | 
				
			||||
 | 
				
			||||
            $order->ship_send = true;
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $order->completed = $order->status == OrderShipStatusEnum::RECEIVED;
 | 
				
			||||
 | 
				
			||||
        return view('user.orders.show', compact('order'));
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function completeOrder(Order $order, Request $request)
 | 
				
			||||
    {
 | 
				
			||||
        $star = intval($request->input('star'));
 | 
				
			||||
        $content = $request->input('content');
 | 
				
			||||
        if ($star < 0 || $star > 5) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('无效的评分');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if (empty($content)) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('请至少些一些内容吧');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 判断是当前用户的订单才可以删除
 | 
				
			||||
        $user = auth()->user();
 | 
				
			||||
        if ($order->isNotUser($user->id)) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsBadRequest('你没有权限');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 只有付完款的订单,而且必须是未完成的, 确认收货
 | 
				
			||||
        if (
 | 
				
			||||
            // 必须是已经付款,且已经确认收货的
 | 
				
			||||
            ! ($order->status == OrderStatusEnum::PAID && $order->ship_status == OrderShipStatusEnum::RECEIVED)
 | 
				
			||||
        ) {
 | 
				
			||||
            return responseJsonAsBadRequest('订单当前状态不能完成');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $orderDetails = $order->details()->get();
 | 
				
			||||
        $comments = $orderDetails->map(function (OrderDetail $orderDetail) use ($user, $content, $star) {
 | 
				
			||||
 | 
				
			||||
            return [
 | 
				
			||||
                'order_id' => $orderDetail->order_id,
 | 
				
			||||
                'order_detail_id' => $orderDetail->id,
 | 
				
			||||
                'product_id' => $orderDetail->product_id,
 | 
				
			||||
                'user_id' => $user->id,
 | 
				
			||||
                'score' => $star,
 | 
				
			||||
                'content' => $content,
 | 
				
			||||
            ];
 | 
				
			||||
        });
 | 
				
			||||
 | 
				
			||||
        DB::beginTransaction();
 | 
				
			||||
 | 
				
			||||
        try {
 | 
				
			||||
 | 
				
			||||
            // 订单完成
 | 
				
			||||
            $order->status = OrderStatusEnum::COMPLETED;
 | 
				
			||||
            $order->save();
 | 
				
			||||
 | 
				
			||||
            // 评论内容
 | 
				
			||||
            Comment::query()->insert($comments->all());
 | 
				
			||||
 | 
				
			||||
            OrderDetail::query()->where('order_id', $order->id)->update(['is_commented' => true]);
 | 
				
			||||
 | 
				
			||||
            // 完成订单增加积分
 | 
				
			||||
            (new ScoreLogServe)->completeOrderAddScore($order);
 | 
				
			||||
 | 
				
			||||
            DB::commit();
 | 
				
			||||
        } catch (\Exception $e) {
 | 
				
			||||
 | 
				
			||||
            return responseJsonAsServerError('服务器异常,请稍后再试');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return responseJson(200, '完成订单已增加积分');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function confirmShip(Order $order)
 | 
				
			||||
    {
 | 
				
			||||
        // 判断是当前用户的订单才可以删除
 | 
				
			||||
        if ($order->isNotUser(auth()->id())) {
 | 
				
			||||
            abort(403, '你没有权限');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($order->status != OrderStatusEnum::PAID) {
 | 
				
			||||
 | 
				
			||||
            return back()->withErrors('订单未付款');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($order->ship_status != OrderShipStatusEnum::DELIVERED) {
 | 
				
			||||
 | 
				
			||||
            return back()->withErrors('订单未发货');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $order->ship_status = OrderShipStatusEnum::RECEIVED;
 | 
				
			||||
        $order->save();
 | 
				
			||||
 | 
				
			||||
        return back()->with('status', '收货成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function cancelOrder(Order $order)
 | 
				
			||||
    {
 | 
				
			||||
        // 判断是当前用户的订单才可以删除
 | 
				
			||||
        if ($order->isNotUser(auth()->id())) {
 | 
				
			||||
            abort(403, '你没有权限');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        if ($order->status != OrderStatusEnum::UN_PAY) {
 | 
				
			||||
 | 
				
			||||
            return back()->withErrors('未付款的订单才能取消');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        $pay = Pay::alipay(config('pay.ali'));
 | 
				
			||||
 | 
				
			||||
        try {
 | 
				
			||||
            $orderData = [
 | 
				
			||||
                'out_trade_no' => $order->no,
 | 
				
			||||
            ];
 | 
				
			||||
            $result = $pay->cancel($orderData);
 | 
				
			||||
 | 
				
			||||
            $order->status = OrderStatusEnum::UN_PAY_CANCEL;
 | 
				
			||||
            $order->save();
 | 
				
			||||
 | 
				
			||||
        } catch (\Exception $e) {
 | 
				
			||||
 | 
				
			||||
            return back()->withErrors('服务器异常,请稍后再试');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
        return back()->with('status', '取消成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 获取积分和钱的换比例
 | 
				
			||||
     *
 | 
				
			||||
     * @return mixed
 | 
				
			||||
     */
 | 
				
			||||
    protected function getScoreRatio()
 | 
				
			||||
    {
 | 
				
			||||
        $scoreRule = ScoreRule::query()->where('index_code', ScoreRuleIndexEnum::COMPLETE_ORDER)->firstOrFail();
 | 
				
			||||
 | 
				
			||||
        return $scoreRule->score ?? 1;
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 地址是否存在
 | 
				
			||||
     *
 | 
				
			||||
     * @param $address
 | 
				
			||||
     * @return bool
 | 
				
			||||
     */
 | 
				
			||||
    protected function hasAddress($address)
 | 
				
			||||
    {
 | 
				
			||||
        return Address::query()
 | 
				
			||||
                      ->where('user_id', auth()->id())
 | 
				
			||||
                      ->where('id', $address)
 | 
				
			||||
                      ->exists();
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    public function destroy($id)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $order Order
 | 
				
			||||
         */
 | 
				
			||||
        $order = Order::query()->findOrFail($id);
 | 
				
			||||
        // 判断是当前用户的订单才可以删除
 | 
				
			||||
        if ($order->isNotUser(auth()->id())) {
 | 
				
			||||
            abort(403, '你没有权限');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        // 支付的订单不能删除
 | 
				
			||||
        if (! in_array($order->status, [OrderStatusEnum::UN_PAY_CANCEL, OrderStatusEnum::TIMEOUT_CANCEL, OrderStatusEnum::COMPLETED])) {
 | 
				
			||||
 | 
				
			||||
            abort(403, '订单不能删除');
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        $order->delete();
 | 
				
			||||
 | 
				
			||||
        return back()->with('status', '删除成功');
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
@ -0,0 +1,67 @@
 | 
				
			||||
<?php
 | 
				
			||||
 | 
				
			||||
namespace App\Http\Controllers\User;
 | 
				
			||||
 | 
				
			||||
use App\Enums\SettingKeyEnum;
 | 
				
			||||
use App\Exceptions\OrderException;
 | 
				
			||||
use App\Http\Controllers\Controller;
 | 
				
			||||
use App\Http\Requests\StoreOrderRequest;
 | 
				
			||||
use App\Jobs\CancelUnPayOrder;
 | 
				
			||||
use App\Models\Address;
 | 
				
			||||
use App\Models\Car;
 | 
				
			||||
use App\Models\Order;
 | 
				
			||||
use App\Models\Product;
 | 
				
			||||
use App\Models\User;
 | 
				
			||||
use Carbon\Carbon;
 | 
				
			||||
use Illuminate\Support\Facades\DB;
 | 
				
			||||
use Jenssegers\Agent\Agent;
 | 
				
			||||
use Yansongda\Pay\Pay;
 | 
				
			||||
 | 
				
			||||
class PaymentController extends Controller
 | 
				
			||||
{
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 再次支付的接口
 | 
				
			||||
     *
 | 
				
			||||
     * @param $id
 | 
				
			||||
     * @return \Symfony\Component\HttpFoundation\Response
 | 
				
			||||
     */
 | 
				
			||||
    public function againStore($id)
 | 
				
			||||
    {
 | 
				
			||||
        /**
 | 
				
			||||
         * @var $masterOrder Order
 | 
				
			||||
         */
 | 
				
			||||
        $masterOrder = Order::query()->findOrFail($id);
 | 
				
			||||
 | 
				
			||||
        // 生成支付信息
 | 
				
			||||
        return $this->buildPayForm($masterOrder, (new Agent)->isMobile());
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
    /**
 | 
				
			||||
     * 生成支付订单
 | 
				
			||||
     *
 | 
				
			||||
     * @param Order $order
 | 
				
			||||
     * @param       $isMobile
 | 
				
			||||
     * @return \Symfony\Component\HttpFoundation\Response
 | 
				
			||||
     */
 | 
				
			||||
    protected function buildPayForm(Order $order, $isMobile)
 | 
				
			||||
    {
 | 
				
			||||
        // 创建订单
 | 
				
			||||
        $order = [
 | 
				
			||||
            'out_trade_no' => $order->no,
 | 
				
			||||
            'total_amount' => $order->amount,
 | 
				
			||||
            'subject' => $order->name,
 | 
				
			||||
        ];
 | 
				
			||||
 | 
				
			||||
        $pay = Pay::alipay(config('pay.ali'));
 | 
				
			||||
 | 
				
			||||
        if ($isMobile) {
 | 
				
			||||
 | 
				
			||||
            return $pay->wap($order);
 | 
				
			||||
        }
 | 
				
			||||
 | 
				
			||||
        return $pay->web($order);
 | 
				
			||||
    }
 | 
				
			||||
 | 
				
			||||
 | 
				
			||||
}
 | 
				
			||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue