您的当前位置:首页正文

Lumen业务篇:接口开发之代码分层(模块化结构),实例演示!

来源:要发发知识网

一、前言

  1. 最近赶在5月1的上线大关,一直没有来得及更新。
  2. 前段时间,有读者建议我出一个 Lumen 项目示例,目前只实现了部分功能,后面有时间慢慢补全逻辑和完善注释。
  3. 同样也要借此机会,介绍一下 Module(模块化) 的项目结构,针对本文,具体的目录层次如下:
/app
....../Base            # 定义基础类
....../Common          # 定义常量、全局函数
....../Middlewares
....../Modules
....../....../Job      # Job模块
....../....../Task     # Task模块
....../Providers
............others............
/bootstrap
/config                # 配置文件目录
/database
/routes
......others.......

二、说明

  1. 还没有书写注释和完整的业务逻辑,所以新手可能理解难度较高。
  2. 本文的项目背景是 ,基于 Lumen 开箱即用的 队列 实现。
  3. 项目开发环境使用最新的 Lumen 5.8.x 以及 PHP 7.2.16 版本。
  4. 快速上手,可以使用本人至今 'Windows' 下正常开发使用的。

三、开始

  1. 确定本地已经安装了 Composer 环境。

  2. 注意,git clone 完成后要执行 composer install 安装依赖。

  3. 目前已经实现的功能如下:

添加定时任务 查询定时任务 定时任务列表
{{base_url}}/api/task/add {{base_url}}/api/task/seek/{task_uuid} {{base_url}}/api/task/all

5.1. 添加定时任务接口

HTTP/1.1 200 OK. POST {{base_url}}/api/task/add

@请求模板: 
{
   "callback_url": "http://127.0.0.1:8001",
   "callback_header": {
       "Content-Type": "application/json"
   },
   "callback_time": "2019-4-22 16:21:51",
   "task_title": "test task",
   "callback_body": {
       "course_id": 1,
       "lesson_id": 25
   }
}
@成功响应: 
{
   "status_code": 200,
   "data": {
       "task_uuid": "54ddf0cceecac4b3349c3f3bffab1f8e",
       "status": "待执行",
       "task_title": "test task",
       "created_at": "2019-04-30 11:18:20",
       "first_execute": "",
       "success_execute": "",
       "failure_execute": ""
   }
}

5.2. 查询定时任务接口

HTTP/1.1 200 OK. POST {{base_url}}/api/task/seed/{task_uuid}
task_uuid = 54ddf0cceecac4b3349c3f3bffab1f8e

@成功响应: 
{
   "status_code": 200,
   "data": {
       "task_uuid": "54ddf0cceecac4b3349c3f3bffab1f8e",
       "status": "待执行",
       "task_title": "test task",
       "created_at": "2019-04-30 11:18:20",
       "first_execute": "",
       "success_execute": "",
       "failure_execute": ""
   }
}

5.1. 定时任务列表接口

HTTP/1.1 200 OK. POST {{base_url}}/api/task/all

@成功响应: 
{
   "status_code": 200,
   "data": {
       "pagination": {
           "total": 1,
           "count": 1,
           "per_page": 10,
           "current_page": 1,
           "last_page": 1
       },
       "data": [
           {
               "task_uuid": "de70b9b9bf5fb13ff6e96776839ceb15",
               "status": "待执行",
               "task_title": "test task",
               "created_at": "2019-04-30 10:54:27",
               "first_execute": "",
               "success_execute": "",
               "failure_execute": ""
           }
       ]
   }
}
  1. 贴出核心 'TaskJob.php' 下的代码,前往 ,可以查看完整代码文件。
<?php

namespace App\Modules\Task;

use App\Jobs\BaseJob;
use App\Modules\Job\JobModel;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;

class TaskJob extends BaseJob
{
    const MAX_ATTEMPTS = 5;

    /**
     * @var array
     */
    private $taskData;

    /**
     * @var TaskModel
     */
    private $taskInstance;

    /**
     * @var JobModel
     */
    private $jobInstance;

    /**
     * @var mixed
     */
    private $response;

    public function __construct(array $taskData)
    {
        $this->taskData = $taskData;
        $this->setTaskInstance();
        $this->setJobInstance();
    }

    public function handle()
    {
        $this->posting() ? $this->successEnding() : $this->errorEnding();
        $this->delete();
        return false;
    }

    private function setTaskInstance()
    {
        $taskContent = json_encode(Arr::except($this->taskData, ['task_title']));
        $filled = ['task_uuid' => md5($taskContent . time()), 'task_content' => $taskContent, 'task_title' => $this->taskData['task_title']];
        $this->taskInstance = (new TaskModel)->createOne($filled);
    }

    private function setJobInstance()
    {
        $this->jobInstance = (new JobModel)->createOne();
    }

    public function getJobInstance()
    {
        return $this->jobInstance;
    }

    public function getTaskInstance()
    {
        return $this->taskInstance;
    }

    private function makeHeaders()
    {
        $temp = Arr::pull($this->taskData, 'callback_header');
        $return = [];
        foreach ($temp as $key => $value) {
            $return[] = $key . ':' . $value;
        }
        return $return;
    }

    private function posting()
    {
        $url = Arr::pull($this->taskData, 'callback_url');
        $body = Arr::pull($this->taskData, 'callback_body');
        $headers = $this->makeHeaders();

        $this->getTaskInstance()->activeOne();
        $this->getJobInstance()->attempts($this->attempts());

        // begin:
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
        $this->response = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        // end:

        return ($httpCode == 200);
    }

    /**
     * @author AdamTyn
     * @description 处理成功结果
     *
     * @return void
     */
    private function successEnding()
    {
        Log::alert(PHP_EOL . PHP_EOL . PHP_EOL . '<Success in 定时系统回调> at ' . date('Y-m-d H:i:s') . PHP_EOL . '<具体响应内容:> ' . 'detail=> ' . PHP_EOL . $this->response . PHP_EOL . PHP_EOL . PHP_EOL);

        $this->getTaskInstance()->successOne($this->response);
    }

    /**
     * @author AdamTyn
     * @description 处理失败结果
     *
     * @return void
     */
    private function errorEnding()
    {
        $log = PHP_EOL . PHP_EOL . PHP_EOL . '<Error in 定时系统回调> at ' . date('Y-m-d H:i:s') . PHP_EOL . '<具体错误内容:> ' . 'detail=> ' . PHP_EOL . $this->response . PHP_EOL . PHP_EOL . PHP_EOL;
        Log::error($log);

        if ($this->attempts() == self::MAX_ATTEMPTS) {
            $this->getTaskInstance()->failureOne($this->response);
        } else {
            $this->release(mt_rand(5, 20));// 失败的定时任务,会推迟随机秒数后再执行
        }
    }
}

四、结语

  1. 本教程面向新手,更多教程会在日后给出。
  2. 随着系统升级,软件更新,以后的配置可能有所变化,在下会第一时间测试并且更新教程;
  3. 欢迎联系在下,讨论建议都可以,之后会发布其它的教程。
  4. 后面紧锣密鼓地将会推出 系列的教程,敬请期待。