Write the Code. Change the World.

1月 31

开始安装 ant design pro

流程参考

https://blog.vini123.com/359

关于路由这块,需要再说明一下。路由有 laravel 的路由,还有 ant design pro 的路由,以及 nginx 的 location 的。

既然后台访问的域名为 xxx/admin/ , 而接口也是 xxx/admin/,这个时候就需要针对该情况的 nginx 的配置。根据 nginx 的 location 的优先级以及正则等原则,可以配置nginx的匹配规则如下:

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

# 后台接口统一加了 api
    location ^~ /admin/api/ {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location /admin/  {
        index index.html index.htm;
        try_files $uri $uri/ /admin/index.html;
    }

# 编辑重启
sudo vim /etc/nginx/sites-available/admin.com
sudo nginx -s reload

ant design pro 相关

话说,要用就用最新的 V4(pro v4 以及 ant design v4),要用就用 TypeScript 。话说,还是得从零开始学一下 TypeScript。 那么之前做的部分工作,可以先暂停。

https://zhuanlan.zhihu.com/antdesign

https://www.zhihu.com/topic/20178853/hot

https://zhuanlan.zhihu.com/p/67498559

那么,我们把之前的 js 版本的 ant design pro 干掉。然后重启一个分支来做 typescript 版本的 ant design pro

# 先看看哪个提交时初始化创建 ant design pro
git log -n 6 --oneline

# 找到对应的 hash,回滚回去
git reset --hard xxxx

# 强提交
git push --force

# 开始新建分支
git checkout -b antdesign

# 所有的 ant design pro 相关的全在这个分支上干

npm create umi

# 选择 ant design pro, 选择 typescript ,y

npm install

# 再继续

git add .
git commit -m 'ant design pro v4 initialize'
git push --set-upstream origin antdesign

当使用 define 的方式定义全局常亮,typescript 再非定义位置报错问题处理。 需要在 typings.d.ts 中配置。
参考: https://pro.ant.design/docs/environment-variables-cn#%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E4%BB%A3%E7%A0%81%E4%B8%AD%E6%8A%A5%E9%94%99%E7%9A%84%E5%A4%84%E7%90%86%E6%96%B9%E5%BC%8F

ant design pro

一边学,一边使用 ant design pro。看到不明白的就开始查查查。

第一个 useState
useState(0) 是最新的 hooks api

https://segmentfault.com/a/1190000018781083?utm_source=tag-newest

1月 31

web 页面,以及后台接口,权限基础架构已经完成。现在再多增点东西。比如发短信,比如模型事件。

先看模型事件,用模型事件一方面是减少耦合,另一方面如果模型多出 Create ,并在 Created 后要做一系列逻辑。这个时候用模型事件就比较好。否则有重复代码,或你用 trait 去处理。反正就是模型事件好。

模型事件文档

# 我们先给 User 用户创建一个观察者
php artisan make:observer UserObserver --model=Models/User

# 创建成功后,删除掉其他的事件,只保留 created 
# 在你希望观察的模型上使用 observe 方法注册观察者。也可以在服务提供者的 boot 方法注册观察者。这里在 AppServiceProvider 中注册观察者。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Models\User;
use App\Observers\UserObserver;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }
}

# 这样,当 User created 的时候,就会调用到事件侦听

再来处理短信相关的。

短信提供商有很多,我们选择用阿里云的。因为短信价格都差不多,服务器,域名都在阿里下。所以短信也选择阿里的。这里使用 overtrue 的 easy-sms 来做。

composer require overtrue/easy-sms

vim config/easysms.php

<?php

return [
    // HTTP 请求的超时时间(秒)
    'timeout' => 10.0,

    // 默认发送配置
    'default' => [
        // 网关调用策略,默认:顺序调用
        'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,

        // 默认可用的发送网关
        'gateways' => [
            'aliyun',
        ],
    ],
    // 可用的网关配置
    'gateways' => [
        'errorlog' => [
            'file' => storage_path('logs/easy-sms/easy-sms.log'),
        ],
        'aliyun' => [
            'access_key_id' => env('SMS_ALIYUN_ACCESS_KEY_ID'),
            'access_key_secret' => env('SMS_ALIYUN_ACCESS_KEY_SECRET'),
            'sign_name' => '约拍宝',
            'templates' => [
                'register' => env('SMS_ALIYUN_TEMPLATE_REGISTER'),
            ]
        ],
    ],
];

# 再来创建 serverprovider

php artisan make:provider EasySmsServiceProvider

<?php

namespace App\Providers;

use Overtrue\EasySms\EasySms;
use Illuminate\Support\ServiceProvider;

class EasySmsServiceProvider extends ServiceProvider
{
    public function boot()
    {

    }

    public function register()
    {
        $this->app->singleton(EasySms::class, function ($app) {
            return new EasySms(config('easysms'));
        });

        $this->app->alias(EasySms::class, 'easysms');
    }
}

# 在 config/app.php 中加入 
App\Providers\EasySmsServiceProvider::class,

# 最后,env 中配置好对应的值即可

使用

use Overtrue\EasySms\EasySms;

public function send(EasySms $easySms)
    {
        $phone = 13888888888;
            // 生成4位随机数,左侧补0
        $code = str_pad(random_int(1, 9999), 4, 0, STR_PAD_LEFT);

        try {
            $result = $easySms->send($phone, [
                'template' => config('easysms.gateways.aliyun.templates.register'),
                'data' => [
                    'code' => $code
                ],
            ]);
        } catch (\Overtrue\EasySms\Exceptions\NoGatewayAvailableException $exception) {
            $message = $exception->getException('aliyun')->getMessage();
            abort(500, $message ?: '短信发送异常');
        }
    }

先到这里,真实场景再使用。下一步就是开始后台了。这里使用阿里的 ant design pro。

顺便再说一下。env 中加入的配置,我们先要在 env example
中先加一份。 .env 是不加入版本控制的。掉了就没了。 .env.example 是加入了版本控制,但敏感数据空着就好,空留key即可。

1月 27

完成了 web 的布局以及权限配置,现在要着手接口了。因为使用 ant design pro,进行前后端分离的项目。之前做了 User 模型的权限控制,这里想改成 Admin 模型,然后 jwt 基于 User 模型和 Admin 模型实现两套接口。这样之前的模型需要修改,以及需要创建 Admin 模型。

实现过程

# 创建模型,迁移以及控制器
php artisan make:model Models/Admin -mc

# 制作迁移文件,执行迁移,给 Admin 加白名单,并把之前 User 模型中的 hasRole 移到 Admin 模型中。最终的数据见下边

# CreateAdminsTable 迁移
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateAdminsTable extends Migration
{
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id')->unique();
            $table->string('nickname', 128);
            $table->string('phone', 16);
            $table->string('address', 128)->nullable();
            $table->string('remark', 128)->nullable();
            $table->unsignedBigInteger('operator_user_id');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->nullableTimestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

# Admin 模型
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Traits\HasRoles;

class Admin extends Model
{
    use HasRoles;

    protected $guard_name = 'admin';

    protected $fillable = ['user_id', 'nickname', 'phone', 'address', 'remark', 'operator_user_id'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

虽然上边这么做了。但是细节还是蛮多的。下边一一说明。

  1. 在 Admin 模型中,加入 `protected $guard_name = ‘admin’;

  2. 在 config/permission.php 中加入 'guard_name' => 'admin',。seeder 的时候会从这里取值。

  3. 在 config/auth.php 中 加入 guard 配置。

# 复制一份 web,修改 provider
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],

# 定义 provider
'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],

# 暂时先这样,然后执行迁移回滚再seeder。调用 tinker 看 role 就晓得了。

php artisan migrate:refresh --seed

php artisan tinker

$user = User::first();
# 这里就可以看到全部权限了
$user->admin->getAllPermissions ();

# 在 seeder 文件中,我们创建 role 以及 permision 的时候,都指定了具体的 guard_name 为 admin。

$guard_name = config('permission.guard_name') ?? 'web';

# 先这样吧,下一步, 我们安装 jwt,然后修改 guard。 这个时候 jwt 后台会使用到,将来接口也使用到。想要两者兼容,可以看下边的链接。

https://learnku.com/articles/28881

安装 jwt

这里操作。

# 安装 jwt
composer require tymon/jwt-auth:1.0.0-rc.5

# 生成 jwt secret
php artisan jwt:secret

# 修改 config/auth.php 中的 guard,指定 admin 的 driver 为jwt

        'admin' => [
            'driver' => 'jwt',
            'provider' => 'admins',
        ],

# 然后修改 Admin 模型,实现 JWTSubject

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Traits\HasRoles;
use Tymon\JWTAuth\Contracts\JWTSubject;

class Admin extends Model implements JWTSubject
{
    use HasRoles;

    protected $guard_name = 'admin';

    protected $fillable = ['user_id', 'nickname', 'phone', 'address', 'remark', 'operator_user_id'];

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return ['role' => 'admin'];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

# 将来用到接口的时候 User 模型一样要这波操作。

看起来好像好了,那么我们到 tinker 中操作一波试试。

php artisan tinker;

use App\Models\Admin;
use Auth;

$admin = Admin::first();
$token = Auth::guard('admin')->login($admin);

# 这个时候,发现报错了。原来是 Admin 这个模型模型没有继承 Auth,我们复制 User 模型这的 Auth 过来,然后继承一下。完整如下。

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
use Tymon\JWTAuth\Contracts\JWTSubject;

class Admin extends Authenticatable implements JWTSubject
{
    use HasRoles;

    protected $guard_name = 'admin';

    protected $fillable = ['user_id', 'nickname', 'phone', 'address', 'remark', 'operator_user_id'];

    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return ['role' => 'admin'];
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}


# 修改好之后,我们再一波操作。发现生成的 token 是 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hZG1pbi5jb20iLCJpYXQiOjE1ODAzMTI1MTUsImV4cCI6MTU4MDMxNjExNSwibmJmIjoxNTgwMzEyNTE1LCJqdGkiOiJsR25sZmlmcW9iSWFwQnc5Iiwic3ViIjoxLCJwcnYiOiJkZjg4M2RiOTdiZDA1ZWY4ZmY4NTA4MmQ2ODZjNDVlODMyZTU5M2E5Iiwicm9sZSI6ImFkbWluIn0.AOU3iiIgGHE6XuaioCDo3Ce8mZmRe63WLYKL_BDj1_w

# 我们复制 token 到 https://jwt.io/ 中,进行 decode,就可以还原出具体的数据 

    {
 typ: "JWT",
 alg: "HS256"
}.
{
 iss: "http:\/\/admin.com",
 iat: 1580312515,
 exp: 1580316115,
 nbf: 1580312515,
 jti: "lGnlfifqobIapBw9",
 sub: 1,
 prv: "df883db97bd05ef8ff85082d686c45e832e593a9",
 role: "admin"
}.
[signature]

# jwt 组成的 三部分都在

到了这里,部分细节要说一遍。

  1. 建立 Admin 模型。安装 jwt ,以及之前已经安装的 laravel permission。让 Admin 模型适应 jwt 以及 laravel permission。
  2. 新建 中间件。以区分 token 是授权 api 的 还是 admin 的。后边会列出来。
  3. 建立路由。默认有 web 和 api 两个路由。可以建一个 admin 的路由。然后在 RouteServiceProvider 中复制一份 api 的修改成 admin 的即可。

列出来

# 新增路由配置
    // 新增 admin 路由
    protected function mapAdminRoutes()
    {
        Route::prefix('admin')
            ->middleware('api')
            ->namespace($this->namespace)
            ->group(base_path('routes/admin.php'));
    }

# 中间件
<?php

namespace App\Http\Middleware;

use Closure;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;

class JWTRoleAuth extends BaseMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param $request
     * @param Closure $next
     * @param null $role
     * @return mixed
     */
    public function handle($request, Closure $next, $role = null)
    {
        try {
            // 解析token角色
            $token_role = $this->auth->parseToken()->getClaim('role');
        } catch (JWTException $e) {
            /**
             * token解析失败,说明请求中没有可用的token。
             * 为了可以全局使用(不需要token的请求也可通过),这里让请求继续。
             * 因为这个中间件的责职只是校验token里的角色。
             */
            return $next($request);
        }

        // 判断token角色。
        if ($token_role != $role) {
            throw new UnauthorizedHttpException('jwt-auth', 'User role error');
        }

        return $next($request);
    }
}

# 并且要在 Kernel 的 routeMiddleware 中进行注册。
'jwt.role' => \App\Http\Middleware\JWTRoleAuth::class,

上边这些弄好了,就开始处理路由。

# 路由 admin.php
<?php

Route::namespace('Admin')->group(function(){

    Route::post('authorizations', 'AuthorizationsController@store')->name('api.authorizations.store');

    Route::group([
        'middleware' => ['jwt.role:admin', 'jwt.auth'],
    ], function () {
        // 刷新token
        Route::put('authorizations/current', 'AuthorizationsController@update')->name('authorizations.update');

        // 删除token
        Route::delete('authorizations/current', 'AuthorizationsController@destroy')->name('authorizations.destroy');
    });
});

测试发现,返回的是 html代码,并非是 json 数据。这个时候需要手动添加 Head 头 指定 Accept 为 application/json 才返回 json 格式。既然这样,就加一个中间件来返回 json 数据。

中间件

<?php

namespace App\Http\Middleware;

use Closure;

class AcceptHeader
{
    public function handle($request, Closure $next)
    {
        $request->headers->set('Accept', 'application/json');

        return $next($request);
    }
}

并添加到 api 中间件组中,

        'api' => [
            \App\Http\Middleware\AcceptHeader::class,
            'throttle:60,1',
            'bindings',
        ],

# admin 路由 和 api 都将会用到这个中间件组。

这样在请求中,就不必每次指定 Header 头的 Accept 值。

1月 27

做好之前的功能,现在开始做权限管理以及后台。权限管理这里使用第三方扩展包 laravel-permission

laravel-permission github

https://docs.spatie.be/laravel-permission/v3/installation-laravel/

按照文档,进行常规操作。

安装使用 laravel-permission

基本步骤如下

composer require spatie/laravel-permission

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

为了更好的使用权限,在 权限表中加了一些额外的字段

# permissions 迁移中加入下边字段
$table->string('display_name');
$table->string('route')->nullable();
$table->integer('icon_id')->nullable();
$table->integer('parent_id')->default(0);
$table->integer('sort')->default(0);

# roles 迁移中加入下边字段
$table->string('display_name');

因为会用到 icon,所以得额外加一个 icons 表。生成一个额外的迁移文件

php artisan make:migration create_icons_tabel --create=icons

# 填充迁移文件
        Schema::create('icons', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('unicode')->nullable();
            $table->string('class')->nullable();
            $table->string('name')->nullable();
            $table->integer('sort')->default(0);
            $table->nullableTimestamps();
        });

# 执行迁移
php artisan migrate

怎么使用呢,看 wiki https://docs.spatie.be/laravel-permission/v3/basic-usage/basic-usage/

  1. 在模型中加入 Spatie\Permission\Traits\HasRoles trait,可以是 User 模型也可以是其他,看你怎么用。
  2. 虽然可以给单独模型赋予权限,不建议这样做。建议通过 role 来实现。先给 role 赋予权限,再给用户赋予角色。这样更好管理和控制权限。

完成了上边的操作,我们还需要做两个数据填充(Seeders),一个 IconSeeder 和 PermissionSeeder 。

php artisan make:seeder IconSeeder

php artisan make:seeder PermissionSeeder

既然要整 icon ,那我们就用阿里的 iconfonts 。去 https://www.iconfont.cn/ 操作一波。

制作好了之后,先创建 json 文件:

vim public/backstage/json/icons.json

# 填充以下内容(以3个icon为例)
[{"unicode": "", "name": "控制台", "class": "icon-kongzhitai"},
 {"unicode": "", "name": "权限", "class": "icon-quanxian"},
 {"unicode": "", "name": "仪表盘", "class": "icon-yibiaopan"}]

再去编辑 IconSeeder。

# 创建 icon
php artisan make:model Models/Icon

# 白名单
protected $fillable = ['unicode','class','name','sort'];

# IconSeeder
<?php

use Illuminate\Database\Seeder;
use App\Models\Icon;

class IconSeeder extends Seeder
{
    public function run()
    {
        Icon::truncate();
        $file = file_get_contents(public_path().'/backstage/json/icons.json');
        $icons = json_decode($file, true);
        foreach ($icons as $icon){
            Icon::create($icon);
        }
    }
}

PermissionSeeder 的迁移文件可参考: https://docs.spatie.be/laravel-permission/v3/advanced-usage/seeding/

根据需求,先暂时建立几个权限以及角色。迁移文件如下:

php artisan make:model Models/Permission

php artisan make:model Models/Role

# Permission Model
<?php

namespace App\Models;

use Spatie\Permission\Models\Permission as SPermission;

class Permission extends SPermission
{
    //菜单图标
    public function icon()
    {
        return $this->belongsTo('App\Models\Icon', 'icon_id', 'id');
    }

    //子权限
    public function childs()
    {
        return $this->hasMany('App\Models\Permission', 'parent_id', 'id');
    }
}

# Role Model
<?php

namespace App\Models;

use Spatie\Permission\Models\Role as SRole;

class Role extends SRole
{

}

然后,permission seeder。

<?php

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\PermissionRegistrar;
use App\Models\User;
use App\Models\Permission;
use App\Models\Role;
use DB;
use Hash;

class PermissionSeeder extends Seeder
{
    public function run()
    {
        app(PermissionRegistrar::class)->forgetCachedPermissions();

        $tableNames = config('permission.table_names');
        Model::unguard();
        foreach ($tableNames as $tableName) {
            DB::table($tableName)->delete();
        }
        Model::reguard();

        $user = User::where('account', '13671638524')->first();
        if (!$user) {
            $user = User::create([
                'account' => '13671638524',
                'password' => Hash::make('w123456'),
                'email' => 'lichking_lin86@qq.com',
                'phone' => '13671638524'
            ]);
        }

        $role = Role::create([
            'name' => 'root',
            'display_name' => '站长'
        ]);

        $user->assignRole($role);

        $permissions = [
            [
                'name' => 'home.manager',
                'display_name' => '主页',
                'route' => '',
                'icon_id' => '1',
                'child' => [
                    [
                        'name' => 'home',
                        'display_name' => '控制台',
                        'route' => 'admin.home',
                        'icon_id' => '12',
                        'child' => []
                    ]
                ]
            ],
            [
                'name' => 'system.manage',
                'display_name' => '系统管理',
                'route' => '',
                'icon_id' => '2',
                'child' => [
                    [
                        'name' => 'system.permission',
                        'display_name' => '权限管理',
                        'route' => 'admin.system.permission',
                        'icon_id' => '2',
                        'child' => [
                            ['name' => 'system.permission.create', 'display_name' => '添加权限', 'route' => 'admin.system.permission.create'],
                            ['name' => 'system.permission.edit', 'display_name' => '编辑权限', 'route' => 'admin.system.permission.edit'],
                            ['name' => 'system.permission.destroy', 'display_name' => '删除权限', 'route' => 'admin.system.permission.destroy'],
                        ]
                    ],
                    [
                        'name' => 'system.role',
                        'display_name' => '角色管理',
                        'route' => 'admin.system.role',
                        'icon_id' => '3',
                        'child' => [
                            ['name' => 'system.role.create', 'display_name' => '添加角色', 'route' => 'admin.system.role.create'],
                            ['name' => 'system.role.edit', 'display_name' => '编辑角色', 'route' => 'admin.system.role.edit'],
                            ['name' => 'system.role.destroy', 'display_name' => '删除角色', 'route' => 'admin.system.role.destroy'],
                            ['name' => 'system.role.permission', 'display_name' => '分配权限', 'route' => 'admin.system.role.permission'],
                        ]
                    ],
                    [
                        'name' => 'system.manager',
                        'display_name' => '管理员',
                        'route' => 'admin.system.manager',
                        'icon_id' => '123',
                        'child' => [
                            ['name' => 'system.manager.create', 'display_name' => '添加管理', 'route' => 'admin.system.manager.create'],
                            ['name' => 'system.manager.edit', 'display_name' => '编辑管理', 'route' => 'admin.system.manager.edit'],
                            ['name' => 'system.manager.destroy', 'display_name' => '删除管理', 'route' => 'admin.system.manager.destroy'],
                            ['name' => 'system.manager.role', 'display_name' => '分配角色', 'route' => 'admin.system.manager.role'],
                            ['name' => 'system.manager.permission', 'display_name' => '分配权限', 'route' => 'admin.system.manager.permission'],
                        ]
                    ]
                ]
            ]
        ];

        foreach ($permissions as $pem1) {
            //生成一级权限
            $p1 = Permission::create([
                'name' => $pem1['name'],
                'display_name' => $pem1['display_name'],
                'route' => $pem1['route'] ?? '',
                'icon_id' => $pem1['icon_id'] ?? 1,
            ]);
            //为角色添加权限
            $role->givePermissionTo($p1);
            if (isset($pem1['child'])) {
                foreach ($pem1['child'] as $pem2) {
                    //生成二级权限
                    $p2 = Permission::create([
                        'name' => $pem2['name'],
                        'display_name' => $pem2['display_name'],
                        'parent_id' => $p1->id,
                        'route' => $pem2['route'] ?? 1,
                        'icon_id' => $pem2['icon_id'] ?? 1,
                    ]);
                    //为角色添加权限
                    $role->givePermissionTo($p2);
                    if (isset($pem2['child'])) {
                        foreach ($pem2['child'] as $pem3) {
                            //生成三级权限
                            $p3 = \App\Models\Permission::create([
                                'name' => $pem3['name'],
                                'display_name' => $pem3['display_name'],
                                'parent_id' => $p2->id,
                                'route' => $pem3['route'] ?? '',
                                'icon_id' => $pem3['icon_id'] ?? 1,
                            ]);
                            //为角色添加权限
                            $role->givePermissionTo($p3);
                        }
                    }
                }
            }
        }

        //初始化的角色
        $roles = [
            ['name' => 'manager', 'display_name' => '管理员']
        ];

        foreach ($roles as $role) {
            Role::create($role);
        }
    }
}

再在 DatabaseSeeder 中加入:

        $this->call(IconSeeder::class);
        $this->call(PermissionSeeder::class);

# 然后迁移回滚操作
php artisan migrate:refresh --seed -v

添加中间件。在 app/Http/Kernel.php 中的 routeMiddleware 增加映射

        'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
        'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
1月 26

完成了注册和登录,现在来完成完整邮箱验证

参考

https://blog.vini123.com/288

https://blog.vini123.com/333

按照上述参考操作,先将 User 模型实现 MustVerifyEmail,再修改路由,加入中间件 verified

Route::namespace('Web')->middleware(['verified'])->group(function() {

    Route::get('/', 'PageController@home')->name('home');

    Route::get('search', 'PageController@search')->name('search');

    Route::get('/users/{user}', 'UserController@index')->name('users.show');

    Route::group(['middleware' => 'auth'], function() {
        Route::get('/users/{user}/edit', 'UserController@edit')->name('users.edit');
    });
});

访问首页,发现会自动跳转到验证邮箱页面。然后点击重新发送验证。会发现有下边的报错:

Expected response code 250 but got code "530", with message "530 5.7.1 Authentication required
 "

这个是 email 相关的配置没好,我们可以使用 qq 邮箱、163邮箱等进行 SMTP 发送邮件。以 qq 邮箱来说,首先得去自己的 qq 邮箱里去申请开通。这里直接列出邮箱的配置数据。

vim .env

# 填充好下边的配置
MAIL_DRIVER=smtp
MAIL_HOST=smtp.qq.com
MAIL_PORT=465
MAIL_USERNAME=xxx@xxxx.com
MAIL_PASSWORD=xxxxx
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=xxxx@xxxx.com
MAIL_FROM_NAME=xxxxxx

当这些配置好了,再发送时,提示 新的验证链接已发送到您的 E-mail。 邮件已经发送。点击,跳转验证。

如果想验证成功后出现提示,可以在 EventServiceProvider中增加:

        \Illuminate\Auth\Events\Verified::class => [
            \App\Listeners\EmailVerified::class,
        ],

然后,自定义 mailVerified 类。添加好上边的,再执行 php artisan event:generate 会自动生成,然后填充方法。

    public function handle(Verified $event)
    {
        // 会话里闪存认证成功后的消息提醒
        session()->flash('success', '邮箱验证成功 ^_^');
    }

之前,弄的样式以及 layouts/app.blade.php 有问题,需要稍微修改。这里懒的弄了。下一步,权限管理,使用 spatie/laravel-permission

1月 25

上边完成了基本布局,发现某些 js,css缺失。这个时候,需要补进来。然后,完成服务端的注册登录逻辑,以及 gee 验证。

修补

加入以下文件

static/style/css/nprogress.css
static/style/css/nprogress.min.css

static/style/js/fn.js
static/style/js/gt.js
static/style/js/nprogress.min.js

部分调整

  1. 将 nprogress.min.js 和 app.js 的加载顺序更换一下。因为 app.js 中会使用到 nprogress 的封装
git add .
git commit -m '修复引入js,css不存在的bug'

认证脚手架

php artisan ui:auth

# 因为之前已经有了 layouts 相关的文件,这个时候会提示是否要覆盖,输入 no 回车。这个时候,会补入和修改相关的文件

# 查看变更的文件
git status 

# 删掉其他新增文件,只保留 resources/views/auth/ 下的文件
# web.php  路由也处理一下。之前就已经好了,这次注入是重复的,可以删掉重复的部分。

做好上边这些,然后开始修改 user 表的迁移文件,以及将 User模型移动到 app\Models\ 下。
继续阅读

1月 19

构建 bootstrap 框架,并定制好基本布局

composer require laravel/ui --dev

php artisan ui bootstrap

以上命令做了以下事情:

  1. 在 npm 依赖配置文件 package.json 中引入 bootstrap、jquery、popper.js 作为依赖;
  2. 修改 resources/js/bootstrap.js ,在此文件中初始化 Bootstrap UI 框架的 JS 部分;
  3. 修改 resources/sass/app.scss 以加载 Bootstrap 的样式文件;
  4. 新增 resources/sass/_variables.scss 样式配置文件。

运行 Laravel Mix

Laravel Mix 一款前端任务自动化管理工具,使用了工作流的模式对制定好的任务依次执行。Mix 提供了简洁流畅的 API,让你能够为你的 Laravel 应用定义 Webpack 编译任务。Mix 支持许多常见的 CSS 与 JavaScript 预处理器,通过简单的调用,你可以轻松地管理前端资源。

使用 Mix 很简单,首先你需要使用以下命令安装 npm 依赖即可。我们将使用 Yarn 来安装依赖,在这之前,因为国内的网络原因,我们还需为 NPM 和 Yarn 配置安装加速:

npm config set registry=https://registry.npm.taobao.org
yarn config set registry https://registry.npm.taobao.org

# 查看镜像
npm get registry 
yarn config get registry

yarn install

yarn run watch-poll

下边进行基础布局
继续阅读

1月 19

从零开始构建 laravel 项目

包裹以下功能:

  1. web 页面(可能是商城,可能是论坛,可能是其他)
  2. 接口功能(一套接口(jwt),提供给 app,小程序 或其他)
  3. 后台功能 (不再使用 layui admin ,使用 ant design pro。权限控制,laravel 自己的权限控制很好,ant design pro 不熟,不知两边的权限能否融洽。就是只使用服务端的权限,不去耦合 antdesignpro 的权限)
  4. 额外。 微信授权、站点地图、elastsearch、定时任务、swoole等。

开始

开发环境依然是 homestead。虽然 docker 很火热, 但总用不好。还有,如果真是线上环境,宁愿一个一个去编译安装所用软件(个数不多)。因为不熟悉,所以不敢放心去使用。

homestead 配置域名:admin.com

laravel 版本为最新版本 6.8.*

# 创建 laravel 6.8 项目
composer create-project laravel/laravel --prefer-dist admin.com "6.8.*"

cd admin.com

# 去掉原本的 readme,添加简单的 readme
rm -rf README.md

echo 'laravel 6.8 initialize' >> Readme.md

# 初始化 git
git init

# 添加,并 commit
git add .

git commit -m 'laravel 6.8 initialize'

为了保证代码风格,安装使用 editorcofig 。配置文件 laravel 项目里边已经有了再稍微改一点点。如下,保存提交版本。

修改根目录下的 .editorconfig

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.yml]
indent_size = 2

[*.{js,html,blade.php,css,scss}]
indent_style = space
indent_size = 2

然后提交版本:

git add .
git commit -m '增加 editorconfig 配置'

修改时区以及语言版本

# vim config/app.php

'timezone' => 'Asia/Shanghai',

'locale' => 'zh-CN',

# git
git add .

git commit -m '设置时区以及默认语言版本'

添加辅助函数

# 在 app 目录下,添加 helpers.php 文件,辅助函数都放在该文件里。
touch app/helpers.php

# 修改 composer.json,让 autoload 的时候载入辅助函数

{
    ...

    "autoload": {
        "psr-4": {
            "App\\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories"
        ],
        "files": [
            "app/helpers.php"
        ]
    }
    ...
}

# 重新加载文件
composer dump-autoload

git提交

git add .

git commit -m '新增辅助函数文件'

现在 helpers.php 文件还是空的,但后续可以添加你需要的。比如 curl 等。

1月 18

站点地图有助于 seo 优化,一般站点都会制作站点地图。 Laravel 项目,有制作站点地图的插件 Laravelium-sitemap。拿来用即可。

laravelium-sitemap wiki

看文档,根据不同的 laravel 版本安装不同的包。

了解点

  1. 可以直接展示站点地图或分包展示地图。也就是直接 xml 或 sitemap index。

  2. 可以缓存。不是每次请求都要生一个。

  3. 可以添加图片,视频等资源。

实践产品

逻辑里边,两种展示方式都添加了。只是选择了直接使用 xml
https://www.yuepaibao.net/sitemap.xml

1月 11

从零开始构建 node 项目

拥有 node,开始从零构建 node 项目吧

mkdir nodeservice

cd nodeservice

npm init

# 输入项目介绍,关键词,auth这些,一路回车,yes,成功创建 package.json 文件。
# 此时仅有该文件

然后按照需要,安装必须用到的包。少了,后边可以继续安装。

npm install express

npm install lodash

npm install moment

npm install mysql

npm install redis

npm install ioredis

npm install request

npm install socket.io

npm install socket.io-redis

npm install socketio-jwt

npm install async

npm install url

npm install jsonwebtoken

npm install bluebird

npm install china-time

npm install winston

npm install mqtt

然后创建 server.js

touch server.js

参考

https://blog.csdn.net/u013165804/article/details/80568626

https://www.jianshu.com/p/5690ebdd18fa

兜底node.js异常

unhandledRejection