Plugin Develop

从框架结构上理解,在后端「控制台」中的“控制面板”、“移动应用”、“功能插件”这三个频道均是插件,只不过业务方向有所不同,开发本质上没有区别。

文件Directory Structure

DemoPlugin/         //插件文件夹(以 uniKey 命名,大驼峰格式)
├── Installer.php       //安装文件,负责处理插件的安装、升级、卸载等操作
├── PluginConfig.php    //安装配置文件,负责定义插件的基础属性和命令字属性字段
├── Plugin.php          //插件主文件,负责实现插件业务逻辑
├── Routes/             //路由文件目录
│   ├── FsRouteApi.php      //插件 API 端路由文件
│   └── FsRouteWeb.php      //插件 Web 端路由文件
├── Controllers/         //控制器目录
│   └── CustomNaming.php    //控制器文件(自定义命名,不限数量)
├── Providers/          //服务提供者目录
│   └── CustomNaming.php    //构造函数文件(自定义命名,不限数量)
├── Components/         //视图组件目录
│   └── CustomNaming.php    //组件控制器文件(自定义命名,不限数量)
├── CustomNaming/       //业务目录文件夹(自定义命名,不限数量)
│   └── CustomNaming.php    //业务文件(自定义命名,不限数量)
├── assets/             //前端资源文件
│   ├── fresns.png          //插件封面图(正方形),必须存在且位置固定
│   ├── style.css           //样式文件
│   └── css/                //文件夹(自定义文件夹)
│       └── style.css           //文件
├── views/              //插件前后端界面文件
│   ├── welcome.blade.php   //Blade 视图模板文件
│   ├── components/         //视图组件目录
│   │   └── plugin.blade.php    //Blade 视图组件模板
│   └── plugin/             //文件夹(自定义文件夹)
│       └── plugin.blade.php    //Blade 视图模板文件
├── lang/               //插件多语言文件目录
│   ├── en/                 //英语(以配置表语言标签为标准)
│   │   └── filename.php        //使用方式参见 Laravel 官方文档
│   ├── zh-hans/            //简体中文
│   │   └── filename.php        //使用方式参见 Laravel 官方文档
│   └── zh-hans/            //繁体中文
│       └── filename.php        //使用方式参见 Laravel 官方文档
├── LICENSE             //版权文件(非必须)
└── README.md           //说明文件(非必须)

文件分发介绍

安装包文件夹分发到执行位置
assets//public/assets/{unikey}/
views//resources/views/plugins/{unikey}/
lang/{语言标签}/resources/lang/{语言标签}/{unikey}/
其余的/app/Plugins/{unikey}/

文件说明

Install.php

  • 继承自 BaseInstaller
class Installer extends BaseInstaller
{
    protected $pluginConfig;

    // 初始化
    public function __construct()
    {
        $this->pluginConfig = new PluginConfig();
    }

}
1
2
3
4
5
6
7
8
9
10
11

PluginConfig.php

  • 继承自 BasePluginConfig
class PluginConfig extends BasePluginConfig
{
    public $type = 2; //2.扩展插件 / 3.移动应用插件 / 4.控制面板插件
    public $uniKey = "DemoPlugin"; //插件标识名,全局唯一值,大驼峰命名
    public $name = '演示插件';
    public $description = "这是一个官方演示插件";
    public $author = "唐杰";
    public $authorLink = "https://tangjie.me";
    public $currVersion = '1.0.0'; //语义化版本号
    public $currVersionInt = 1; //整型版本号
    public $sceneArr = [
        //参见开发者文档「数据库」 plugins 插件表里的介绍
    ];
    public $accessPath = "/mall"; //定义前端访问路径,支持变量,详情参见「数据库」 plugins 插件表里的介绍
    public $settingPath = "/mall/admin"; //定义设置页访问路径

    //插件默认命令字
    public CONST FRESNS_CMD_DEFAULT = 'fresns_cmd_default';

    //插件命令字回调映射
    CONST FRESNS_CMD_HANDLE_MAP = [
        self::FRESNS_CMD_DEFAULT => 'defaultHandler',
    ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Web 业务逻辑开发

1、添加 Web 路由

<?php

use App\Http\Center\Helper\PluginHelper;

// 判断插件是否可用
if (PluginHelper::pluginCanUse('DemoPlugin')) {
    Route::group(['prefix' => 'demoplugin', 'namespace' => '\App\Plugins\DemoPlugin\Demo'], function () {
        Route::get('/index', '[email protected]')->name('demoplugin.web.index');
    });
}
1
2
3
4
5
6
7
8
9
10

2、根据路由添加 Web 控制器

  • 业务代码的文件夹组织形式开发者根据自身习惯自定义即可
  • 控制器继承自 BasePluginWebController
<?php

namespace App\Plugins\DemoPlugin\Demo;

use App\Http\Center\Base\BasePluginWebController;
use App\Plugins\DemoPlugin\PluginConfig;
use Illuminate\Http\Request;

class DemoWebController extends BasePluginWebController
{

    public function index(Request $request){
        $pluginConfig = new PluginConfig();

        $data = [
            'demo' => 'demo',
            'message' => trans('DemoPlugin/auth.test'),
            'currentTime' => date("Y-m-d H:i:s", time()),
        ];

        return $this->displayDemoPlugin($pluginConfig->uniKey, 'welcome', $data);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

3、视图文件介绍

  • 视图界面基于 Laravel Blade 模板标准。
  • 技术层面不分前后台,只是访问入口的区别,通过 PluginConfig.php 文件定义两种入口路径,accessPathsettingPath
  • 如果有 settingPath 入口,路径需要提供 {token} 变量名,用于后台访问时传参登录凭证。
  • 无论哪种入口,视图界面的模板文件放在安装包 views 文件夹中,多语言文件放在安装包 lang 文件夹中。

4、多语言处理

  • 前后台多语言凭 URL 参数识别,格式为 lang={语言标签},例如 lang=en
  • 多语言标签标准,参见多语言唯一性逻辑
  • 多语言可以程序固定在语言文件中(参见 lang 文件夹),也可以采用数据库中的配置(参见 多语言的语言包 JSON 格式 介绍)。

API 业务逻辑开发

如果插件有 API 需求,可以直接在插件中开发 API 业务,无需独立再开发 API 插件。

1、添加 API 路由

<?php

use App\Http\Center\Helper\PluginHelper;

// 判断插件是否可用
if (PluginHelper::pluginCanUse('DemoPlugin')) {
    Route::group(['prefix' => 'demoplugin', 'namespace' => '\App\Plugins\DemoPlugin\Demo'], function () {
        Route::get('/index', '[email protected]')->name('demoplugin.web.index');
    });
}
1
2
3
4
5
6
7
8
9
10

2、根据路由添加 API 控制器

  • 业务代码的文件夹组织形式开发者根据自身习惯自定义即可
  • 控制器继承自 BasePluginApiController
<?php

namespace App\Plugins\DemoPlugin\Demo;

use App\Http\Center\Base\BasePluginApiController;
use Illuminate\Http\Request;

class DemoApiController extends BasePluginApiController
{

    public function test(Request $request){
        $data = [
            'demo' => 'demo',
            'curr_time' => date("Y-m-d H:i:s", time()),
        ];

        $this->success($data);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

3、请求签名规则

  • 插件 API 的签名和主程序 API 的签名,逻辑一样。
// header 中参与签名的参数
CONST SIGN_FIELD_ARR = [
    'platform',
    'version',
    'versionInt',
    'timestamp',
    'uid',
    'mid',
    'token',
    'appId',
];
1
2
3
4
5
6
7
8
9
10
11
  • 1、根据 header 中的 appId 参数,查询获得 app_secret
  • 2、将 SIGN_FIELD_ARR 中的参数按照参数名 ASCII 码从小到大排序(字典序),使用 URL 键值对的格式(即 key1=value1&key2=value2… )拼接成字符串 stringA
  • 3、在 stringA 最后拼接上 &key={app_secret} 得到待签名字符串 stringSignTemp
  • 4、对 stringSignTemp 进行 MD5 运算,得到签名值 signValue
// 对数组的值按key排序
ksort($dataMap);
// 生成url的形式
$params = http_build_query($dataMap);
// 生成sign
$sign = md5($params . "&key={$signKey}");
1
2
3
4
5
6

4、请求接口,返回数据格式

  • 接口状态码 message 支持多语言,可参见数据库 code_messages
{
    "code": 0,
    "message": "ok",
    "data": {
        //返回数据内容
    }
}
1
2
3
4
5
6
7

命令字开发

插件每个功能逻辑都可封装为一个命令字,详情请参见 命令字通讯 说明。

1、添加命令字

  • PluginConfig.php 文件中添加命令字
// 演示命令字
public CONST FRESNS_CMD_DEMO = 'fresns_cmd_demo';
1
2

2、添加回调函数

  • 2.1、在 PluginConfig.php 文件中添加命令字回调函数
// 插件命令字回调映射
CONST FRESNS_CMD_HANDLE_MAP = [
    self::FRESNS_CMD_DEFAULT => 'defaultHandler',
    self::FRESNS_CMD_DEMO => 'demoHandler',
];
1
2
3
4
5
  • 2.2、在 Plugin.php 文件中添加命令字回调函数
// 演示
protected function demoHandler($input) {
    LogService::info("插件处理开始: ", $input);

    $data = [
        'time'  => date("Y-m-d H:i:s", time()),
    ];

    LogService::info("插件处理完成: ", $input);

    return $this->pluginSuccess($data);
}
1
2
3
4
5
6
7
8
9
10
11
12

3、使用命令字

  • 在其代码位置通过 RPC 调用插件命令字
$cmd = PluginConfig::FRESNS_CMD_DEMO;

$input = [
    'email' => $email,
    'title' => $title,
    'content' => $content
];

$resp = CmdRpcHelper::call(FresnsCmdWords::class, $cmd, $input);

if(CmdRpcHelper::isErrorCmdResp($resp)){
    $this->errorCheckInfo($resp);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

4、返回命令字处理结果

  • 4.1、处理成功返回
return $this->pluginSuccess($data);
1
  • 4.2、处理失败返回状态码和描述(状态码描述支持多语言,参见数据库 code_messages 表)
return $this->pluginError(ErrorCodeService::CODE_EXCEPTION);
1