管理后台后端动作审核接口实现


从这篇教程开始,我们将开始为 Roast 应用构建管理后台,在上一篇教程中,我们为咖啡店的增删改查加上了权限管理,普通用户针对咖啡店的新增、修改和删除请求都会在后台管理员审核后才能执行,否则就是躺在数据库里的一条记录而已,我们将从这个功能出发构建我们的后台管理系统。

由于整个工作量比较大,所以我们会分三篇教程来实现这个管理后台和审核功能,首先我们还是从 Laravel 后端接口开始,先来提供后端通过和拒绝 API 接口。

第一步:新增拦截非后台管理员的中间件

在正式编写 API 接口前,我们先来创建一个中间件 Owner,用于拦截非管理员用户对管理后台发起的请求:

php artisan make:middleware Owner

编写新生成的中间件类 app/Http/Middleware/Owner.php 代码如下:

<?php

namespace App\Http\Middleware;

use App\User;
use Closure;
use Illuminate\Support\Facades\Auth;

class Owner
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  string|null $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {
        if (Auth::user()->permission < User::ROLE_SHOP_OWNER) {
            abort(403, 'Unauthorized action.');
        }

        return $next($request);
    }
}

如果请求用户的角色是普通用户或商家用户的话,则会返回 403 响应,提示用户没有权限,然后在 app/Http/Kernel.php$routeMiddleware 属性中注册这个新创建的中间件:

protected $routeMiddleware = [
    ... // 其他中间件
    'owner' => \App\Http\Middleware\Owner::class,
];

第二步:注册管理后台路由

创建完中间件之后,接下来就可以在 routes/api.php 中注册管理后台动作审核路由了,我们会在之前的的路由分组基础上新增一个针对管理后台的路由分组,在这个分组中不仅应用了 auth:api 中间件,还应用了新创建的 owner 中间件,以表明访问这个分组中的路由,不仅需要登录,还需要管理员及以上权限:

// 管理后台路由
Route::group(['prefix' => 'v1/admin', 'middleware' => ['auth:api', 'owner']], function () {
    Route::get('/actions', 'API\Admin\ActionsController@getActions');
    Route::put('/actions/{action}/approve', 'API\Admin\ActionsController@putApproveAction');
    Route::put('/actions/{action}/deny', 'API\Admin\ActionsController@putDenyAction');
});

我们定义了三个路由,分别用于获取动作列表、通过指定动作、拒绝某个动作,下面我们就来编写相应的控制器方法实现对应的业务逻辑。

第三步:初始化控制器方法

首先创建 ActionsController 控制器:

php artisan make:controller API/Admin/ActionsController

初始化新生成的 app/Http/Controllers/API/Admin/ActionsController.php 代码如下:

<?php

namespace App\Http\Controllers\API\Admin;

use App\Models\Action;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class ActionsController extends Controller
{
    // 获取所有待审核动作
    public function getActions()
    {

    }

    // 通过审核并执行相应动作
    public function putApproveAction(Action $action)
    {

    }

    // 审核不通过
    public function putDenyAction(Action $action)
    {

    }
}

由于对应的业务逻辑比较复杂,我们先留空实现代码,然后逐个来实现。

第四步:实现获取待审核动作方法

首先实现相对简单的 getActions 方法,用于在后台列表返回所有待审核未处理的动作:

/**
 * 获取所有待审核动作
 */
public function getActions()
{
    // 如果是后台管理员则返回所有未处理操作
    if (Auth::user()->permission >= User::ROLE_ADMIN) {
        $actions = Action::with('cafe')
            ->with('company')
            ->where('status', '=', Action::STATUS_PENDING)
            ->with('by')
            ->get();
    } else {
        // 否则返回归属于该用户的待处理操作
        $actions = Action::with('cafe')
            ->with('company')
            ->whereIn('company_id', Auth::user()->companiesOwned()->pluck('id')->toArray())
            ->where('status', '=', Action::STATUS_PENDING)
            ->with('by')
            ->get();
    }

    return response()->json($actions);
} 

第五步:创建 Action Policy

在实现通过/拒绝审核动作方法前,需要先创建用于动作审核授权的 Action 策略类,以判断用户是否有操作权限:

php artisan make:policy ActionPolicy

编写新生成的策略类 app/Policies/ActionPolicy.php 代码如下:

<?php

namespace App\Policies;

use App\Models\Action;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class ActionPolicy
{
    use HandlesAuthorization;

    /**
     * Create a new policy instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * 如果用户是管理员或超级管理员的话则具备该权限
     * 否则只有审核归属于自己公司名下的咖啡店动作的权限
     *
     * @param \App\User $user
     * @param \App\Models\Action $action
     * @return bool
     */
    public function approve(User $user, Action $action)
    {
        if ($user->permission == User::ROLE_ADMIN || $user->permission == User::ROLE_SUPER_ADMIN) {
            return true;
        } else if ($user->companiesOwned->contains($action->company_id)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 如果用户是管理员或超级管理员的话则具备该权限
     * 否则只有审核归属于自己公司名下的咖啡店动作的权限
     *
     * @param \App\User $user
     * @param \App\Models\Action $action
     * @return bool
     */
    public function deny(User $user, Action $action)
    {
        if ($user->permission == User::ROLE_ADMIN || $user->permission == User::ROLE_SUPER_ADMIN) {
            return true;
        } else if ($user->companiesOwned->contains($action->company_id)) {
            return true;
        } else {
            return false;
        }
    }
}

然后在 app/Providers/AuthServiceProvider.php 中注册这个策略类:

protected $policies = [
    Cafe::class => CafePolicy::class,
    Action::class => ActionPolicy::class
];

第六步:实现通过动作审核方法

注册完 ActionPolicy 之后,就可以在控制器中通过该策略类来判断操作用户是否具备审核动作的权限了,编写控制器 ActionsControllerputApproveAction 方法如下:

/**
 * 执行待处理动作并通过这条动作审核
 * @param Action $action
 * @return \Illuminate\Http\JsonResponse
 */
public function putApproveAction(Action $action)
{
    if (Auth::user()->cant('approve', $action)) {
        abort(403, '该用户没有通过审核权限');
    }

    $cafeService = new CafeService();
    $actionService = new ActionService();
    // 根据操作类型分类处理
    switch ($action->type) {
        case 'cafe-added':
            // 反序列化咖啡店数据
            $newActionData = json_decode($action->content, true);
            // 执行变更
            $cafeService->addCafe($newActionData, $action->user_id);

            // 操作完成后通过这条审核
            $actionService->approveAction($action, Auth::user()->id);
            break;
        case 'cafe-updated':
            // 反序列化咖啡店更新数据
            $actionData = json_decode($action->content, true);

            // 获取更新后数据
            $updatedActionData = $actionData['after'];
            // 执行变更
            $cafeService->editCafe($action->cafe_id, $updatedActionData, $action->user_id);

            // 通过这条审核
            $actionService->approveAction($action, Auth::user()->id);
            break;
        case 'cafe-deleted':
            // 获取要删除的咖啡店数据
            $cafe = $cafe = Cafe::where('id', '=', $action->cafe_id)->first();
            // 执行变更
            $cafe->delete();

            // 通过这条审核
            $actionService->approveAction($action, Auth::user()->id);
            break;
    }

    // 返回响应
    return response()->json('', 204);
}

在具备实现代码中,我们会先判断操作用户是否具备通过审核权限,如果没有则直接返回 403 响应,如果有的才继续往下执行,我们会根据动作类型,比如新增、修改还是删除来执行不同的动作,执行完成后会将条 actions 动作记录标记为通过,具体的实现代码封装到了 ActionService 中:

/**
 * Approves an action
 *
 * @param \App\Models\Action $action Action being approved.
 * @param int processedBy
 */
public function approveAction($action, $processedBy)
{
    $action->status = Action::STATUS_APPROVED;
    $action->processed_by = $processedBy;
    $action->processed_on = Carbon::now();
    $action->save();
}

第七步:实现拒绝动作审核方法

最后是我们的拒绝动作审核方法 putDenyAction,相对通过审核,拒绝审核的业务逻辑相对简单,只需要标记相应的 actions 动作记录为审核不通过就好了:

/**
 * 拒绝动作审核,标记为不通过
 *
 * @param \App\Models\Action $action
 * @return \Illuminate\Http\JsonResponse
 */
public function putDenyAction(Action $action)
{
    if (Auth::user()->cant('deny', $action)) {
        abort(403, '该用户没有拒绝审核权限');
    }

    // 拒绝这条变更请求
    $actionService = new ActionService();
    $actionService->denyAction($action, Auth::user()->id);

    // 返回响应
    return response()->json('', 204);
}

同样标记请求记录为不通过的实现代码封装到了 ActionService 中:

/**
 * Denies an action
 *
 * @param \App\Models\Action $action Action being denied.
 * @param int $processedBy
 */
public function denyAction($action, $processedBy)
{
    $action->status = Action::STATUS_DENIED;
    $action->processed_by = $processedBy;
    $action->processed_on = Carbon::now();
    $action->save();
}

至此,我们就初步完成了管理后台后端咖啡店增加、修改、删除动作审核 API 接口,下一篇我们将开始构建管理后台的前端 UI。


Vote Vote Cancel Collect Collect Cancel

<< 上一篇: 实现简单的、针对咖啡店增删改查的 RBAC 权限管理功能

>> 下一篇: 通过 Vue Router 提供的路由元信息功能实现前端路由权限判断