Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/zh/architecture/backend-layer.md
T
2026-10-08 17:50:29 +08:00

386 lines
17 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 → Service → Model 构成主干链路,FormRequest、横切服务、Middleware 分别提供参数校验、通用能力、请求拦截等支撑。各层职责明确,层间通过方法调用串联,不越层访问。
:::
## 架构全景
```text
┌─────────────────────────────────────────────────────────────────────┐
│ 支撑层 │
│ │
│ FormRequest(参数校验) 横切 Service(通用能力) Middleware(请求拦截)│
│ Http/Requests/*.php Services/(Jwt/Dict…) Http/Middleware/ │
└──────────────────────────────┬──────────────────────────────────────┘
│ 被主干层调用 / 前置拦截
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 主干链路 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Controller(控制器层) │ │
│ │ 注解标注 → 参数注入 → 调用 Service → 封装响应 │ │
│ │ 基类:Controller | 文件:app/Http/Controllers/Api/*.php │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ 调用 Service 方法 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Service(服务层) │ │
│ │ 属性配置 → 唯一性校验 → 生命周期钩子 → 事务 → 调用 Model │ │
│ │ 基类:BaseService / BaseTenantService | Services/*.php │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ 调用 Eloquent ORM │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Model(模型层) │ │
│ │ 表映射 → 软删除 → 审计字段 → 关联关系 → ORM → SQL │ │
│ │ Trait:SoftDeletesByFlag / HasAuditColumns | Models/*.php │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Database(五种数据库:MySQL / PG / SQL Server / Oracle / SQLite)│
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
## 各层职责详解
### Controller 层
**文件**:`app/Http/Controllers/Api/{Module}Controller.php`
**基类**:`App\Http\Controllers\Controller`
**职责**:HTTP 请求入口,只做"编排"不做"判断"
| 职责 | 说明 | 示例 |
|------|------|------|
| 参数注入 | 类型注入 FormRequest 完成格式校验 | `add(ExampleRequest $request)` |
| 调用 Service | 委托业务逻辑(快捷方法一行调用) | `$this->_add($service, $data)` |
| 响应封装 | 统一 JSON 格式返回 | `$this->success()` / `$this->fail()` |
| 注解标注 | 声明权限和日志 | `#[Permission]`、`#[OperationLog]` |
**Controller 不应该做**:业务判断、数据库操作、文件处理、重复的格式校验。
```php
class ExampleController extends Controller
{
public function __construct(protected ExampleService $exampleService) {}
// 标准 CRUD —— 一行调用(构造注入,无需 initialize)
#[OperationLog('示例管理-查询分页', OperationLog::TYPE_QUERY)]
#[Permission('sys:example:page', '示例分页')]
public function page(): JsonResponse
{
return $this->_page($this->exampleService);
}
#[OperationLog('示例管理-新增', OperationLog::TYPE_ADD, '新增示例:{name}')]
#[Permission('sys:example:add', '添加示例')]
public function add(ExampleRequest $request): JsonResponse
{
return $this->_add($this->exampleService, $request->validated());
}
}
```
### Service 层
**文件**:`app/Services/{Module}Service.php`
**基类**:`BaseService`(系统共享)/ `BaseTenantService`(租户隔离)
**职责**:业务核心,通过"属性配置 + 生命周期钩子"声明式 CRUD
| 职责 | 方式 | 说明 |
|------|------|------|
| 行为定义 | 属性配置 | `$pageLikeFields`、`$uniqueFields`、`$serializeMaps` 等 16 个声明式属性 |
| 业务定制 | 生命周期钩子 | `beforeAdd`、`afterUpdate`、`beforeDelete` 等 10 个钩子 |
| 横切处理 | Trait 组合 | 文件字段、字典翻译、数据权限、租户隔离(6 个 Concerns Trait) |
| 数据安全 | `DB::transaction` | add/update/delete/batchDelete 主流程事务包裹 |
**Service 不应该做**:直接 HTTP 交互、直接返回 JSON 响应。
```php
class ExampleService extends BaseService
{
use HasCrudOperations, HasQueryScopes, HasSerializeMaps, HasUniqueCheck;
protected string $modelClass = Example::class;
protected array $pageLikeFields = ['name'];
protected array $pageEqFields = ['status'];
protected array $uniqueFields = ['name'];
protected array $serializeMaps = ['status' => 'example_status'];
// 业务定制:钩子按需重写
protected function beforeAdd(array $data): array
{
$data['sort'] = $data['sort'] ?? 0;
return $data;
}
}
```
### Model 层
**文件**:`app/Models/{TableName}.php`
**基类**:Eloquent Model + Concerns Trait
**职责**:数据持久化,只做表映射和自动维护
| 职责 | 说明 |
|------|------|
| 表映射 | `protected $table = 'example'`(不含前缀,`DB_PREFIX` 自动拼接) |
| 软删除 | `SoftDeletesByFlag` 注册全局范围自动过滤 `is_delete=1` |
| 审计字段 | `HasAuditColumns` 的 `creating/updating` 事件自动填充 |
| 关联关系 | `hasMany`、`belongsTo`、`belongsToMany` 等 |
**Model 不应该做**:业务判断、复杂查询编排(交给 Service)。
```php
class Example extends Model
{
use HasAuditColumns, SoftDeletesByFlag;
protected $table = 'example';
// 关联关系(按需定义)
public function category()
{
return $this->belongsTo(Category::class, 'category_id');
}
}
```
### FormRequest 层(参数校验支撑)
**文件**:`app/Http/Requests/{Module}Request.php`
**基类**:`BaseRequest`
**职责**:参数**格式**校验(必填、类型、长度、枚举),不含业务规则
```php
class ExampleRequest extends BaseRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'type' => 'nullable|integer|in:1,2,3,4',
];
}
}
```
**触发方式**:控制器方法类型注入,方法执行前自动校验;失败由 `failedValidation()` 返回统一 JSON(`code=1` + 字段明细)。
### 横切 Service(通用能力支撑)
**文件**:`app/Services/{Name}Service.php`(横切类)
**职责**:跨模块通用能力,无状态、无副作用
| 服务 | 能力 |
|------|------|
| `JwtService` | 令牌签发/刷新/解析(Redis 有状态) |
| `DictCacheService` | 字典查询(两级缓存) |
| `PasswordService` | 密码加密(bcrypt,成本因子 12) |
| `ExcelService` | Excel 导入导出(表头映射、分批) |
| `CaptchaService` | 图片验证码(IP 限流) |
| `RequestInfoService` | IP 归属地 / OS / 浏览器解析 |
**横切 Service 不应该做**:持有请求状态、直接返回响应。
### Middleware 层(请求拦截支撑)
**文件**:`app/Http/Middleware/*.php`
**职责**:请求拦截,在 Controller 前后执行
```text
ProxyMiddleware → Cors → ForceJsonResponse → Authenticate → ResolveTenant → LogOperation → Controller
```
| 中间件 | 作用 | 注入数据 |
|--------|------|----------|
| `Authenticate` | JWT 认证 + 权限校验 | `attributes['userInfo']` |
| `ResolveTenant` | 租户上下文 | `attributes['tenantId']` / `isSuperAdmin` / `tenantInfo` |
| `LogOperation` | 操作日志(handle 采集,terminate 落库) | — |
详见 [4.2 请求处理链路](/zh/architecture/request-flow.html)。
## 各层方法对照表
### 分页查询链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_page($service)` | HTTP 请求参数 | JsonResponse |
| Service | `pageList($params, $where, $with)` | 参数数组 | `['records'=>[], 'total'=>int, 'current'=>int, 'size'=>int]` |
| Model | `Model::query()->paginate(...)` | 分页条件 | LengthAwarePaginator |
### 新增链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_add($service, $data)` | FormRequest 校验后的数组 | JsonResponse |
| Service | `add($data)` | snake_case 关联数组 | `int`(新记录 ID) |
| Model | `Model::create($data)` | 关联数组 | Model 实例 |
**Service::add 内部流程**(HasCrudOperations):
`beforeAdd` → `checkUnique` → `DB::transaction { Model::create(HasAuditColumns 审计填充)}` → `afterAdd`
### 修改链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_update($service, $id, $data)` | id + 请求数据 | JsonResponse |
| Service | `update($id, $data)` | id + 关联数组 | `bool` |
| Model | `Model::find($id)->update($data)` | 模型 + 关联数组 | `bool` |
### 删除链路
| 层 | 方法 | 输入 | 输出 |
|----|------|------|------|
| Controller | `_delete($service, $id)` | id | JsonResponse |
| Service | `delete($id)` | id | `bool` |
| Model | `$model->softDelete()` | 软删除标记 | `bool`(`UPDATE SET is_delete=1`) |
## 数据流向
### 请求方向(前端 → 后端 → 数据库)
```text
前端请求(camelCase JSON)
│
▼
FormRequest::validated() ← 格式校验(camelCase 规则),原样传递
│
▼
Controller::_add($service, $data) ← 编排 + 注解元数据
│
▼
Service::doAdd 入口 ← 键名统一转为 snake_case
│
▼
Service::beforeAdd($data) ← 钩子:预处理
│
▼
Service::checkUnique($data) ← 唯一性校验(BizException 拦截)
│
▼
DB::transaction {
Service::processTenantId($data) ← 租户填充(HasDataScope 启用时)
Service::processFileFieldsOnSave ← 文件字段处理(HasFileProcessing 启用时)
Model::create($data) ← 审计字段自动填充(creating 事件)
}
│
▼
Database(snake_case)
```
### 响应方向(数据库 → 后端 → 前端)
```text
Database(snake_case 数据)
│
▼
Model ← ORM 查询(软删除全局范围生效)
│
▼
Service 处理(processAfterRead 处理链)
│ processFileFieldsOnRead() ← 文件字段补全域名(启用时)
│ processSerializeMapsList() ← 字典显示名补全(status → statusText)
│ afterDetail()/afterPageList() ← 钩子:补充关联数据
▼
Result::success() / page() ← snake_case → camelCase 转换
│
▼
JSON 响应 → 前端
```
## 异常处理传播
```text
Model 层异常(如数据库连接失败)
│
▼ 向上抛出
Service 层异常
│ beforeAdd/beforeDelete 抛出 BizException(业务规则)
│ 唯一性校验抛出 BizException
│ DB::transaction 自动回滚
▼ 向上抛出
Controller 层
│ 方式一:_add/_update/_delete 等快捷方法捕获 BizException → fail(消息, code)
│ 方式二:不捕获,由全局 Handler 统一处理
▼
Exceptions\Handler::render()
│ BizException → {code:异常code(默认1), msg:"业务提示"}
│ ValidationException → {code:422, msg:"参数验证失败", data:...}
│ AuthenticationException → {code:401}
│ NotFoundHttpException → {code:404}
│ 其它异常 → {code:500, msg:调试原文/生产"操作失败"}
▼
JSON 响应
```
**推荐做法**:
- Controller 通过 `_add` / `_update` / `_delete` 调用时,**不捕获异常**,交给快捷方法与 Handler
- Controller 手写复杂流程时,可 try-catch 捕获 `BizException` 后返回 `$this->fail()`
- Service 层的业务校验直接 `throw new BizException('提示')`,**不要**返回错误码让上层判断
## 完整请求示例
以"新增案例"为例,展示一个请求从进入到返回的完整链路:
```text
POST /api/example/add
Content-Type: application/json
Authorization: Bearer <token>
{"name": "新案例", "type": 1, "status": 1}
```
```text
1. ProxyMiddleware → 真实 IP 识别(受信代理)
2. Cors → 非 OPTIONS,放行(响应时追加 CORS 头)
3. ForceJsonResponse → Accept 强制 JSON
4. Authenticate → 校验 Token → 注入 attributes['userInfo']
→ 反射 #[Permission] → checkPermission('sys:example:add') ✓
5. ResolveTenant → 注入 tenantId / isSuperAdmin
6. LogOperation(handle) → 收集上下文,读 #[OperationLog]
7. ExampleRequest
│ rules() 校验 name/type/status ✓
│ validated() → ["name"=>"新案例", "type"=>1, "status"=>1]
▼
8. ExampleController::add()
│ 注解元数据已由 4/6 读取
│ $this->_add($service, $data)
▼
9. ExampleService::add($data)
│ 入口键名转换 → array_camel_to_snake()
│ beforeAdd($data) → 默认 sort(钩子)
│ checkUnique($data) → name 唯一 ✓
│ DB::transaction {
│ Example::create($data) → creating:create_user/create_time 自动填充
│ }
│ afterAdd($id, $data) → 无额外操作
▼
10. Database INSERT → laravel_example → 自增 ID 42
11. 返回 {"code":0, "ok":true, "msg":"添加成功", "data":{"id":42}}
12. LogOperation(terminate) → 写入 laravel_operation_log(响应后,不影响业务)
```
## 层间通信规范
| 规则 | 说明 |
|------|------|
| Controller → Service | ✅ 允许,标准调用 |
| Controller → Model | ❌ 禁止,越层 |
| Controller → FormRequest | ✅ 允许,类型注入 |
| Service → Model | ✅ 允许,标准调用 |
| Service → 横切 Service | ✅ 允许,调用通用能力(静态/注入均可) |
| Service → Controller | ❌ 禁止,反向依赖 |
| Service → FormRequest | ❌ 禁止,格式校验在 HTTP 入口完成 |
| Model → Service / Controller | ❌ 禁止,反向依赖 |
| Middleware → 后续链路 | ✅ 通过 `request->attributes` 注入数据 |
| FormRequest → 业务规则 | ❌ 禁止,只做格式校验(业务规则在 Service) |