Basic information

Fresns 主程序是基于 PHP Laravel 8 框架和 MySQL 8 数据库而开发的社交网络服务软件,开发扩展插件需要了解以下技术基础资料。

Reference Documents

Directory Structure

fresns/         //主程序根目录
├── app/            //核心代码目录
│   ├── Base/           //基础类
│   ├── Console/        //控制台命令
│   ├── Exceptions/     //异常类
│   ├── Helpers/        //辅助类
│   ├── Http/           //主程序核心功能目录
│   ├── Listeners/      //监测功能目录
│   ├── Plugins/        //插件执行目录
│   ├── Providers/      //服务提供者目录
│   └── Traits/         //自定义特征
├── bootstrap/      //引导程序目录
├── config/         //配置文件目录
├── database/       //数据文件目录
├── extensions/     //扩展安装预备目录(插件和主题)
├── public/         //网站根目录
│   ├── assets/         //扩展插件的资源目录
│   ├── static/         //内置静态文件
│   ├── index.php       //网站入口文件
│   ├── favicon.ico     //站点 ico 图标
│   └── robots.txt      //Robots 协议
├── resources/      //框架资源目录
│   ├── lang/           //语言文件
│   └── views/          //视图文件
│       ├── commons/        //公共模板文件
│       ├── fresns/         //Fresns 控制台模板文件
│       ├── plugins/        //插件视图专用目录
│       └── themes/         //主题模板专用目录
├── routes/         //路由文件目录
├── storage/        //框架存储目录
├── tests/          //框架测试目录
├── .env.example    //配置参数文件模板(使用时修改为 .env 文件名)
├── artisan         //框架命令行接口
├── compose.json    //引用库信息
├── server.php      //服务模拟文件
├── package.json    //前端编译用途文件
├── webpack.mix.js  //前端编译用途文件
├── phpunit.xml     //单元测试用途文件
└── LICENSE         //版权文件

命令字通讯

插件作为一个独立功能模块,从系统设计和业务封装的角度,采用的是「命令字」设计模式,即一个插件模块包括多个命令字,外部通过命令字方式来调用插件。插件与主程序之间,插件与插件之间,均通过命令字通讯,一个完整的通讯流程包括请求输入 input 和结果输出 output

请求输入

参数说明
cmd调用的插件命令字,插件的配置文件中配置
pluginClass插件类
input输入参数,系统内置标准命令字由系统定义,插件根据自身业务功能自定义。
output结果输出,返回插件请求标准格式。
//命令字指令,请求哪个命令字
$cmd = BasePluginConfig::FRESNS_CMD_DEFAULT;

//请求哪个插件
$pluginClass = PluginHelper::findPluginClass($PluginUniKey); //$PluginUniKey 为插件大驼峰名字

//请求的参数列表
$input = [
    'phone' => $phone,
    'template' => $template,
    'variale1' => $variale1,
    'variale2' => $variale2,
];

//请求系统分发命令字请求并返回结果
$resp = CmdRpcHelper::call($pluginClass, $cmd, $input);
if (CmdRpcHelper::isErrorCmdResp($resp)) {
    return $this->pluginError($resp);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

结果输出

参数说明
code状态码
message状态信息(如需多语言,请使用 code_messages 表)
output输出数据
{
    "code": 0,
    "message": "ok",
    "output": {
        //命令字输出数据
    }
}
1
2
3
4
5
6
7

中间处理逻辑

1、配置文件中定义好命令字处理的映射函数

// 插件默认命令字, 任何插件必须要要有
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

2、检查命令字是否存在(在某一个具体的插件中,当请求“字”来了后,检查是否存在)

// 检查命令字
protected function checkPluginCmdExist($cmd){
    $method = $this->pluginCmdHandlerMap[$cmd] ?? '';
    if(empty($method)){
        return false;
    }

    return true;
}
1
2
3
4
5
6
7
8
9

3、如果命令字存在,则根据业务需求进行参数检查(比如是否整数、邮箱之类的)

// 参数校验
if(method_exists($this->pluginConfig, $methodRule)){

    $validRes = ValidateService::validateServerRule($params, $this->pluginConfig->{$methodRule}());

    if($validRes !== true){
        LogService::info("插件请求cmd [$cmd] 参数异常", $validRes);
        return $this->pluginError(BasePluginConfig::CODE_PARAM_ERROR, $validRes);
    }
}
1
2
3
4
5
6
7
8
9
10

参数校验遵循 Laravel 表单验证,可以验证的参数类型及验证方式同 Lavavel 文档。

4、检查通过后,调用「插件命令字回调映射」中的 handler 函数,处理具体逻辑,然后返回结果

// 执行方法
$result = $this->$method($params);
1
2

完整处理步骤示例

// 执行方法调用
public function handle($cmd, $params, $options = []){
    LogService::info("插件请求 cmd [$cmd] 参数", $params);

    // 检查命令字
    if(!$this->checkPluginCmdExist($cmd)){
        return $this->pluginError(BasePluginConfig::HELPER_EXCEPTION_ERROR);
    }

    $method = $this->pluginCmdHandlerMap[$cmd] ?? '';
    $methodRule = $method . "Rule";

    // 参数校验
    if(method_exists($this->pluginConfig, $methodRule)){

        $validRes = ValidateService::validateServerRule($params, $this->pluginConfig->{$methodRule}());

        if($validRes !== true){
            LogService::info("插件请求cmd [$cmd] 参数异常", $validRes);
            return $this->pluginError(BasePluginConfig::CODE_PARAM_ERROR, $validRes);
        }
    }

    // 执行方法
    $result = $this->$method($params);

    LogService::info("插件请求 cmd [$cmd] 结果", $result);

    return $result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

插件验证签名

用户将通过 URL 访问插件页面,URL 中会传参签名信息,用于一键登录。

  • 1、先使用 URL Encode 解码 {sign} 变量名的值,得到 base64 加密字符串
  • 2、再使用 base64 解密字符串,形成 key => value 格式的 header 和 sign 数据
  • 3、调用系统内置命令字验证签名 fresns_cmd_verify_sign
  • 4、调用系统命令字验证 Token fresns_cmd_verify_session_token
  • 5、根据签名和 Token 验证结果,插件自行处理自己的逻辑。
  • 补充资料

定时任务

类型命令字
建立定时任务add_crontab_plugin_item
取消定时任务delete_crontab_plugin_item
  • 1、建立或取消定时任务
    • 1.1、建立定时任务时请求参数
      • 定时任务插件 crontab_plugin_unikey
      • 定时任务插件命令字 crontab_plugin_cmd
      • 定时任务执行周期(分钟)crontab_task_period
    • 1.2、取消定时任务时请求参数
      • 定时任务插件 crontab_plugin_unikey
  • 2、主程序会在每次请求之前检查系统是否有定时任务需要执行,如果需要执行,则执行该定时任务。

订阅事件

类型命令字
建立订阅add_sub_plugin_item
取消订阅delete_sub_plugin_item

订阅数据表新增数据

插件订阅数据表动态(所有数据表均可订阅)。告之主程序订阅哪张表,以及发生动态时,执行自己或者代理插件的哪个命令字;当订阅的数据表发生「新增」记录时,主程序执行插件指定的命令字。

  • 1、建立或取消订阅
    • 1.1、建立订阅时请求参数
      • 订阅类型 subscribe_type = 3
      • 订阅者(插件) subscribe_plugin_unikey
      • 订阅者的命令字 subscribe_plugin_cmd
      • 订阅哪张表 subscribe_table_name
    • 1.2、取消订阅时请求参数
      • 订阅类型 subscribe_type = 3
      • 订阅者(插件) subscribe_plugin_unikey
  • 2、主程序执行时
    • 2.1、在新增数据后,BaseModel 可以获取到 table T 和插入的 id
    • 2.2、执行 FresnsSubPluginConfig 中内置的订阅命令字 fresns_cmd_sub_add_table,该命令字扫描指定的订阅信息;
    • 2.3、如果订阅信息中的 subscribe_type = 3,则调用订阅者 subscribe_plugin_cmd 命令字,传入的参数为 tableNameinsertId
  • 3、主程序发现订阅信息时,会根据插件订阅的信息调用插件的命令字,输入参数是主程序事先根据业务规定好。

订阅用户活跃状态

插件订阅 header 信息中用户和成员请求。告之主程序订阅事件发生后执行自己的哪个命令字;当主程序 API 请求 header 信息中含有用户或成员信息后,主程序执行插件指定的命令字。

  • 1、建立或取消订阅
    • 1.1、建立订阅时请求参数
      • 订阅类型 subscribe_type = 4
      • 订阅者(插件) subscribe_plugin_unikey
      • 订阅者的命令字 subscribe_plugin_cmd
    • 1.2、取消订阅时请求参数
      • 订阅类型 subscribe_type = 4
      • 订阅者(插件) subscribe_plugin_unikey
  • 2、主程序执行时
    • 2.1、在接口 header 中如果有用户或成员参数后,可以获取到 usermemeber 信息;
    • 2.2、执行 FresnsSubPluginConfig 中内置的订阅命令字 fresns_cmd_sub_active_user,该命令字扫描指定的订阅信息;
    • 2.3、如果订阅信息中的 subscribe_type = 4,则调用订阅者 subscribe_plugin_cmd 的命令字,传入的参数为 uidmid
  • 3、主程序发现订阅信息时,会根据插件订阅的信息调用插件的命令字,输入参数是主程序事先根据业务规定好。

订阅命令字行为

插件订阅指定命令字的行为反馈。告之主程序订阅哪个命令字,以及发生行为后,执行自己或者代理插件的哪个命令字;当订阅的命令字发生「执行」操作时,主程序执行插件指定的命令字。

  • 1、建立或取消订阅(目前仅支持订阅 fresns_cmd_direct_release_content 命令字)
    • 1.1、建立订阅时请求参数
      • 订阅类型 subscribe_type = 5
      • 订阅者(插件) subscribe_plugin_unikey
      • 订阅者的命令字 subscribe_plugin_cmd
      • 订阅哪个命令字 subscribe_command_word
    • 1.2、取消订阅时请求参数
      • 订阅类型 subscribe_type = 5
      • 订阅者(插件) subscribe_plugin_unikey
  • 2、主程序执行时
    • 2.1、当命令字执行完操作任务后,会将 tableName、insertId、commandWord 三个信息传参给 fresns_cmd_sub_active_command_word
    • 2.2、命令字 fresns_cmd_sub_active_command_word 接受到指令后,扫描 subscribe_type = 5 的订阅信息;
    • 2.3、如果订阅信息中的 subscribe_command_word = $commandWord,则调用订阅者 subscribe_plugin_cmd 的命令字,传入的参数为 tableNameinsertId
      • tableName = postscomments
      • insertId = 主键 ID