Files
easygoadmin-wiki/EasyGoAdmin_Gin_Layui/zh/architecture/backend-layer.md
T
2026-10-20 21:19:44 +08:00

430 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 4.3 后端分层架构
:::tip 概述
后端采用"三层主干 + 三层支撑"架构:Controller → Logic → Model 构成主干链路,Validate、Service、Middleware 分别提供参数校验、通用能力、请求拦截等支撑。各层职责明确,层间通过方法调用串联,不越层访问。
:::
## 架构全景
```text
┌─────────────────────────────────────────────────────────────────────┐
│ 支撑层 │
│ │
│ Validate(参数校验) Service(通用能力) Middleware(请求拦截) │
│ app/validate/*.php app/service/*.php app/middleware/*.php │
└──────────────────────────────┬──────────────────────────────────────┘
│ 被主干层调用
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 主干链路 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Controller(控制器层) │ │
│ │ 接收请求 → 获取参数 → 调用 Logic → 封装响应 │ │
│ │ 基类:BaseController | 文件:app/controller/*.php │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ 调用 Logic 方法 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Logic(逻辑层) │ │
│ │ 属性配置 → 参数校验 → 生命周期钩子 → 横切处理 → 调用 Model │ │
│ │ 基类:BaseLogic / BaseTenantLogic | 文件:app/logic/*.php │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ 调用 Model ORM │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Model(模型层) │ │
│ │ 表映射 → 软删除 → 审计字段 → ORM → SQL │ │
│ │ 基类:BaseModel | 文件:app/model/*.php │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Database(数据库) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
## 各层职责详解
### Controller 层
**文件**:`app/controller/{Module}Controller.php`
**基类**:`BaseController`
**职责**:HTTP 请求入口,只做"搬运"不做"判断"
| 职责 | 说明 | 示例 |
|------|------|------|
| 参数获取 | 从请求中提取参数 | `$this->getJsonBody()`、`$this->getParams()` |
| 调用 Logic | 委托业务逻辑 | `$this->logic->add($data)` |
| 响应封装 | 统一 JSON 格式返回 | `$this->success()`、`$this->fail()` |
| 注解标注 | 声明权限和日志 | `#[Permission]`、`#[Log]` |
**Controller 不应该做:** 业务判断、数据库操作、文件处理、参数校验(特殊场景除外)。
```php
class ArticleController extends BaseController
{
protected ArticleLogic $logic;
protected function initialize(): void
{
$this->logic = new ArticleLogic();
}
// 标准 CRUD — 一行调用
#[Permission('sys:article:page', '文章分页')]
public function page(): Json
{
return parent::_index($this->logic);
}
#[Permission('sys:article:add', '添加文章')]
public function add(): Json
{
return parent::_add($this->logic, $this->getJsonBody());
}
}
```
### Logic 层
**文件**:`app/logic/{Module}Logic.php`
**基类**:`BaseLogic`(系统共享)/ `BaseTenantLogic`(租户隔离)
**职责**:业务核心,通过"属性配置 + 生命周期钩子"声明式 CRUD
| 职责 | 方式 | 说明 |
|------|------|------|
| 行为定义 | 属性配置 | `$pageLikeFields`、`$uniqueFields`、`$validateClass` 等 |
| 业务定制 | 生命周期钩子 | `beforeAdd`、`afterUpdate`、`beforeDelete` 等 |
| 自动校验 | `$validateClass` | add/update 时自动调用验证器 |
| 横切处理 | 属性驱动 | 文件字段、富文本、枚举映射、数据权限、租户隔离 |
**Logic 不应该做:** 直接 HTTP 交互、直接返回 JSON 响应。
```php
class ArticleLogic extends BaseTenantLogic
{
/**
* 关联的模型类
*
* @var string
*/
protected string $modelClass = Article::class;
/**
* 参数验证器类
*
* @var string
*/
protected string $validateClass = ArticleValidate::class;
/**
* 需要自动处理的文件上传字段
*
* @var array
*/
protected array $fileFields = ['cover'];
/**
* LIKE 模糊匹配字段
*
* @var array
*/
protected array $pageLikeFields = ['title'];
/**
* 精确匹配字段
*
* @var array
*/
protected array $pageEqFields = ['status', 'category_id'];
/**
* 唯一性校验字段
*
* @var array
*/
protected array $uniqueFields = ['title'];
/**
* 枚举显示名映射
*
* @var array
*/
protected array $serializeMaps = ['status' => 'article_status'];
/**
* 新增前处理:设置默认排序值
*
* @param array $data 待新增的数据
* @return array 处理后的数据
*/
protected function beforeAdd(array $data): array
{
$data['sort'] = $data['sort'] ?? 0;
return $data;
}
}
```
### Model 层
**文件**:`app/model/{TableName}.php`
**基类**:`BaseModel`
**职责**:数据持久化,只做表映射和自动维护
| 职责 | 说明 |
|------|------|
| 表映射 | `protected $name = 'article'` |
| 软删除 | 全局查询范围自动过滤 `is_delete=1` |
| 审计字段 | `onBeforeInsert`/`onBeforeUpdate` 自动填充 |
| 关联关系 | `hasMany`、`belongsTo` 等 |
**Model 不应该做:** 业务判断、复杂查询逻辑(交给 Logic)。
```php
class Article extends BaseModel
{
protected $name = 'article';
// 关联关系(按需定义)
public function category()
{
return $this->belongsTo(Category::class, 'category_id');
}
}
```
### Validate 层
**文件**:`app/validate/{Module}Validate.php`
**基类**:`think\Validate`
**职责**:参数格式校验,定义规则 + 场景
```php
class ArticleValidate extends Validate
{
protected $rule = [
'title' => 'require|max:200',
];
protected $message = [
'title.require' => '文章标题不能为空',
];
protected $scene = [
'add' => ['title'],
'update' => ['title'],
];
}
```
**触发方式:** Logic 配置 `$validateClass` 后,add/update 自动调用。
### Service 层
**文件**:`app/service/{Name}Service.php`
**职责**:跨模块通用能力,无状态、无副作用
| 服务 | 能力 |
|------|------|
| `DictService` | 字典查询(两级缓存) |
| `PasswordService` | 密码加密(bcrypt 双重加盐) |
| `JwtService` | Token 生成/刷新/解析 |
| `ExcelService` | Excel 导入导出 |
| `CaptchaService` | 图片验证码 |
**Service 不应该做:** 持有状态、直接操作数据库(通过 Model 静态方法例外)。
### Middleware 层
**文件**:`app/middleware/{Name}Middleware.php`
**职责**:请求拦截,在 Controller 前后执行
```text
CorsMiddleware → AuthMiddleware → TenantMiddleware → DemoMiddleware → LogMiddleware → Controller
```
| 中间件 | 作用 | 注入数据 |
|--------|------|----------|
| `AuthMiddleware` | JWT 认证 + 权限校验 | `$request->userInfo` |
| `TenantMiddleware` | 租户上下文 | `$request->tenantId` |
| `DemoMiddleware` | 演示环境拦截 | — |
| `LogMiddleware` | 操作日志记录 | — |
## 各层方法对照表
### 分页查询链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_index($logic)` | HTTP 请求参数 | Json 响应 |
| Logic | `pageList($params, $where, $with)` | 参数数组 | `['records'=>[], 'total'=>int, ...]` |
| Model | `Model::page($current, $size)->select()` | 分页条件 | Collection |
### 新增链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_add($logic, $data)` 或 `$this->logic->add($data)` | Json 请求体 | Json 响应 |
| Logic | `add($data)` | 关联数组 | `int`(新记录 ID) |
| Model | `Model::create($data)` | 关联数组 | Model 实例 |
**Logic::add 内部流程:**
`beforeAdd` → `validateData` → `camel_to_snake` → `processTenantId` → `processFile` → `processContent` → `filterTableFields` → `checkUnique` → `Model::create` → `afterAdd`
### 修改链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_edit($logic, $id, $data)` | id + Json 请求体 | Json 响应 |
| Logic | `update($id, $data)` | id + 关联数组 | `bool` |
| Model | `Model::find($id)->save($data)` | 模型实例 + 关联数组 | `bool` |
### 删除链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_remove($logic, $id)` | id | Json 响应 |
| Logic | `delete($id)` | id | `bool` |
| Model | `$model->save(['is_delete' => 1])` | 软删除标记 | `bool` |
## 数据流向
### 请求方向(前端 → 后端 → 数据库)
```text
前端请求(camelCase JSON)
│
▼
BaseController::getJsonBody() ← 获取原始参数
│
▼
BaseLogic::beforeAdd($data) ← 钩子:预处理
│
▼
BaseLogic::validateData($data) ← 自动参数校验
│
▼
BaseLogic::convertQueryParams() ← camelCase → snake_case
│
▼
BaseLogic::processFileFieldsOnSave ← 文件字段处理
│
▼
BaseLogic::filterTableFields() ← 过滤非数据库字段
│
▼
BaseLogic::checkUnique() ← 唯一性校验
│
▼
BaseModel::create() ← 审计字段自动填充
│
▼
Database(snake_case)
```
### 响应方向(数据库 → 后端 → 前端)
```text
Database(snake_case 数据)
│
▼
BaseModel ← ORM 查询
│
▼
BaseLogic 处理
│ processFileFieldsOnRead() ← 文件字段补全域名
│ processContentFieldsOnRead() ← 富文本占位符替换
│ processSerializeMaps() ← 枚举显示名补全
│ afterDetail() ← 钩子:补充关联数据
▼
Result::convertData() ← snake_case → camelCase
│
▼
JSON 响应 → 前端
```
## 异常处理传播
```text
Model 层异常(如数据库连接失败)
│
▼ 向上抛出
Logic 层异常(如 beforeDelete 抛出业务异常、validateData 抛出 ValidateException)
│
▼ 向上抛出
Controller 层
│ 方式一:try-catch 捕获,返回 fail()
│ 方式二:不捕获,由 ExceptionHandle 统一处理
▼
ExceptionHandle::render()
│ ValidateException → {"code":1, "msg":"参数验证失败", "data":"..."}
│ ModelNotFoundException → {"code":1, "msg":"数据不存在"}
│ RuntimeException(401) → {"code":1, "msg":"未授权"}
│ 其他异常 → {"code":1, "msg":"操作失败"}
▼
JSON 响应
```
**推荐做法:**
- Controller 通过 `_add`/`_edit`/`_remove` 调用时,不捕获异常,交给 ExceptionHandle
- Controller 直接调用 `$this->logic->add()` 时,try-catch 捕获并返回 `$this->fail()`
- Logic 层的业务异常(如 beforeDelete 校验)直接 `throw new \Exception`
## 完整请求示例
以"新增文章"为例,展示一个请求从进入到返回的完整链路:
```text
POST /api/article/add
Content-Type: application/json
Authorization: Bearer <token>
{"title": "新文章", "categoryId": 1, "status": 0}
```
```text
1. CorsMiddleware → 放行(设置跨域头)
2. AuthMiddleware → 校验 Token → 注入 $request->userInfo
3. TenantMiddleware → 解析租户 → 注入 $request->tenantId
4. LogMiddleware → 记录日志(#[Log] 注解)
5. ArticleController::add()
│ getJsonBody() → ["title"=>"新文章", "categoryId"=>1, "status"=>0]
│
▼
6. ArticleLogic::add($data)
│ beforeAdd($data) → 设置默认 sort=0
│ validateData($data, 'add') → 校验 title 必填 ✓
│ camel_to_snake($data) → ["title"=>"新文章", "category_id"=>1, "status"=>0]
│ processTenantId($data) → 加入 tenant_id
│ processFileFieldsOnSave → cover 无值,跳过
│ filterTableFields($data) → 过滤非表字段
│ checkUnique($data) → title 唯一 ✓
│
▼
7. Article::create($data)
│ onBeforeInsert → 自动填充 create_user, create_time
│
▼
8. Database INSERT
9. ArticleLogic::afterAdd($id, $data) → 无额外操作
10. 返回 {"code":0, "ok":true, "msg":"添加成功", "data":{"id":42}}
```
## 层间通信规范
| 规则 | 说明 |
|------|------|
| Controller → Logic | ✅ 允许,标准调用 |
| Controller → Model | ❌ 禁止,越层 |
| Logic → Model | ✅ 允许,标准调用 |
| Logic → Service | ✅ 允许,调用通用能力 |
| Logic → Controller | ❌ 禁止,反向依赖 |
| Model → Logic | ❌ 禁止,反向依赖 |
| Model → Service | ⚠️ 慎用,仅静态工具方法 |
| Middleware → Controller | ✅ 通过 `$request` 注入数据 |
| Validate → Logic | ❌ 禁止,只做格式校验 |