Appearance
服务端开发指南
本篇将介绍基于 ThinkPHP 8 的项目服务端的基础目录结构、执行流程、以及常用基础模块类库(控制器、中间件、列表类等)的规范与用法,帮助您快速熟悉和开展后端的二次开发。
目录结构
服务端所有的核心逻辑存放在项目的 server/ 文件夹内:
text
├─📂 server // 服务端根目录(管理后台、接口)
│ ├─📂 app // 核心应用目录
│ │ ├─📂 adminapi // 管理后台接口模块
│ │ │ ├─📂 config // 模块独立配置
│ │ │ ├─📂 controller // 控制器
│ │ │ ├─📂 http // 中间件等
│ │ │ ├─📂 listener // 事件监听
│ │ │ ├─📂 lists // 列表分页类
│ │ │ ├─📂 logic // 核心逻辑层
│ │ │ ├─📂 service // 可复用的业务独立服务层
│ │ │ ├─📂 validate // 验证器类
│ │ ├─📂 api // Web端/移动端接口模块
│ │ ├─📂 common // 多模块公共方法与基类
│ ├─📂 public // WEB对外访问目录
│ │ ├─📄 index.php // 框架入库文件
│ │ ├─📂 admin // 已编译的后台前端发布代码入口
│ ├─📄 .env // 数据库配置文件与环境变量运行流程
项目采用彻底的前后端分离设计,服务端只处理数据处理并返回 RESTful 类型的 JSON 数据接口。默认的接口访问 URI 路由映射模式为:
http://服务器或本地域名/模块名称/控制器名称/控制器方法访问
一套基础的生命执行流程如下:
- 接口请求:前端携附带 Token 等发来路由请求。
- 进入应用模块:框架匹配到对应的
app/模块名(如adminapi)。 - 经过中间件:顺序通过模块或全局设定的拦截中间件(如身份防伪、限流)。
- 进入控制器:在控制器对传参进行接收分发。
- 进入验证类/逻辑层/列表类/服务层:如果有业务新增数据,先走一遍
validate类校验安全,随后转交logic等层面执行复杂计算。如果是获取查询列表页,调用专门的lists类型实例操作。 - 响应返回:将返回给前端的数据结构进行统一封装,抛接出结果体。
流程图示:
text
接口请求 --> 进入应用模块 --> 经过安全校验中间件 --> 进入控制器 --> [ 数据验证类验参 --> 逻辑层\列表层\基础服务层 ] --> 控制器拼接执行成功标记响应 -> 返回最终格式结果模块划分
根据 ThinkPHP 8.0 开发规范,每个具体应用的逻辑单独置于 server/app/某个模块 目录下区隔开。现阶段项目分为以下关键内置模块:
adminapi:给 PC 端管理后台(Vue Admin)访问请求的数据接口。api:对外公共移动端或前台侧(Nuxt Web端)访问数据的 API 接口。
控制器 (Controller)
访问
控制器的代码应当编写在模块下对应的 controller 文件夹(例 server/app/adminapi/controller/ 下)。您可以自由再往下根据功能细分文件夹嵌套使用。 访问形式为 "http://域名/模块名称/控制器文件名称/具体写的方法名"。如果是带有二级目录,可以为 "http://域名/模块名称/具体包名.控制器文件名称/方法名" 等框架允许规范。
继承与属性
大多数开发下,我们需要继承项目预留的基础控制器类。 例如在管理后台接口的开发时继承 BaseAdminController(具体类名请根据您的核心组件包为准),系统会在内部执行登录验证并在子类暴露出管理员的核心身份属性。你可以直接用指针提取使用:
php
<?php
namespace app\adminapi\controller;
// 请引入实际项目中定义的基础控制器命名空间路径
use core\base\BaseAdminController;
class TestController extends BaseAdminController
{
// 需要登录身份才能执行的接口
public function index()
{
$admin_id = $this->adminId; // 当前发出请求的管理员ID
$profile = $this->adminInfo; // 当前管理员的所有权限、属性等详细数组
}
}登录与白名单控制
如果是无需判断 JWT Token 也必须放行的接口操作,譬如"账号登录功能主接口"或者基础配置获取,我们可以在控制器内定义一个 $notNeedLogin 数组来忽略安全核验:
php
<?php
namespace app\adminapi\controller;
use core\base\BaseAdminController;
class LoginController extends BaseAdminController
{
// 写入这里的方法名,将不被基础控制器的安全中间件阻挡
public array $notNeedLogin = ['account', 'logout'];
public function account()
{
//…… 登录验证密码分发 Token 相关处理
}
}统一响应
为规范前后端对接的数据契约标准,成功和失败的固定包体格式大致必须包裹如下:
json
{
"code": 1,
"show": 0,
"msg": "执行通过,返回对应数据",
"data": {
"lists": [],
"count": 0,
"page_no": 1,
"page_size": 15,
"extend": []
}
}故您可以通过基类提供的语法糖(便捷封装方法)返回:
php
<?php
namespace app\adminapi\controller;
use core\base\BaseAdminController;
class TestController extends BaseAdminController
{
public function index()
{
return $this->success('成功语录'); // 表示仅执行业务成功
return $this->fail('操作异常'); // 返回警告或拦截停止的代码
return $this->data(['key' => 'val']); // 带纯数据的主体返回
return $this->dataLists(); // 与"列表类"方法混用,返回含有独立分页规范的列表数据
}
}验证类 (Validate)
在系统接收 Request 请求的各种零散参数后,如果马上组装进数据库是非常致命的(存在字段攻击、遗漏),我们要求使用验证器统一强过滤参数。
实现步骤:
validate目录新建业务专属验证继承类BaseValidate。- 控制器中实例化后,直接通过封装方法如
goCheck($scene)调用。
定义验证规则示例:
php
<?php
namespace app\adminapi\validate\auth;
use core\base\BaseValidate;
class AdminValidate extends BaseValidate
{
protected $rule = [
'name' => 'require|max:20',
];
protected $message = [
'name.require' => '名称不能为空',
'name.max' => '名称不得超过20个字符',
];
// 新增时指定针对性的场景方法名
public function sceneAdd()
{
return $this->only(['name']);
}
}控制器中执行验参拦截并接收安全参数:
php
public function add()
{
// post()表示针对请求过来的 body 类型参数做拦截校验
// goCheck("add") 结合在验证类写好的 sceneAdd 定义规则强验,如果不符合上面 `$message` 报错信息会直接向前端顶穿退出;符合则返回安全合规的关联数组数据供逻辑进一步组装使用。
$params = (new AdminValidate())->post()->goCheck('add');
// ...往下再调用 logic 追加数据库写入逻辑...
}分页列表类 (Lists)
处理大量的列表、数据导出、排序,如果仍将长代码写在控制器里则不利于阅读。项目引入了 Lists 规范。
实现步骤:
- 在模块下找到
lists目录,并新建列表类并继承基础功能如BaseAdminDataLists(带有具体使用分页规范与返回格式);如果对数据还需要搜索要求,就实现接口(implements ListsSearchInterface)重写特定方法。 - 控制器中将实例以参数塞进
$this->dataLists()。
开发示例:
php
<?php
namespace app\adminapi\lists\auth;
use core\base\BaseAdminDataLists;
use core\lists\ListsSearchInterface;
class AdminLists extends BaseAdminDataLists implements ListsSearchInterface
{
// 定义可以向前端放开的搜索匹配条件字段规则
public function setSearch(): array
{
return [
'%like%' => ['name', 'account'],
];
}
// 重写获取当前切割片查询后的列表数据条目
public function lists(): array
{
// $this->searchWhere, $this->limitOffset等来自于基类里系统底层帮你计算完毕得到的结果直接使用即可。
return Admin::where($this->searchWhere)
->limit($this->limitOffset, $this->limitLength)
->select()
->toArray();
}
// 根据条件记录数据表此时满足条件的全部数据行总个数总和
public function count(): int
{
return Admin::where($this->searchWhere)->count();
}
}极其简化的控制器引入列表与查询逻辑:
php
public function lists()
{
// 将整个查询过程委托给对应的 List类实例处理并以统一 Json 格式对外分发。
return $this->dataLists(new AdminLists());
}