Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/zh/guide/controller.md
T
2026-10-08 17:50:29 +08:00

281 lines
10 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.
# 3.6 控制器开发
:::tip 概述
控制器是 HTTP 请求的入口,负责参数获取、调用 Service、返回统一 JSON 响应。通过 `#[Permission]` 和 `#[OperationLog]` 注解实现权限校验和操作日志,配合基类快捷方法,单个接口通常只需几行代码。
:::
## 控制器文件位置
```text
app/Http/Controllers/Api/ExampleController.php
```
## 完整代码(来自项目实际代码)
```php
<?php
namespace App\Http\Controllers\Api;
use App\Attributes\OperationLog;
use App\Attributes\Permission;
use App\Http\Controllers\Controller;
use App\Http\Requests\ExampleRequest;
use App\Services\ExampleService;
use Illuminate\Http\JsonResponse;
/**
* 示例管理控制器
*
* 负责示例相关的接口入口,包含分页、列表、详情、新增、修改、删除、批量删除,
* 以及数据导入、导出。
* 具体业务逻辑委托给 ExampleService,控制器仅做参数校验、
* 权限与日志标注及响应封装。
*
* 说明:
* - 所有接口通过 #[Permission] 注解声明权限标识与描述,由框架统一鉴权。
* - 所有接口通过 #[OperationLog] 注解声明操作日志(类型、模板),由框架统一记录。
* - 导入 / 导出接口的日志类型分别为 TYPE_IMPORT、TYPE_EXPORT,与常规 CRUD 区分。
* - 常规 CRUD 复用基类的
* _page / _list / _detail / _add / _update / _delete / _batchDelete 方法。
* - 导入 / 导出走 ExampleService 的 import / export 专用方法。
*/
class ExampleController extends Controller
{
/**
* 构造函数
*
* 注入示例业务服务,供各接口方法复用。
*
* @param ExampleService $exampleService 示例业务服务
*/
public function __construct(protected ExampleService $exampleService) {}
/**
* 查询分页
*
* 对应权限:sys:example:page,操作日志类型:查询。
* 返回分页结构,适合列表页展示。
*
* @return JsonResponse 分页数据
*/
#[OperationLog('示例管理-查询分页', OperationLog::TYPE_QUERY)]
#[Permission('sys:example:page', '示例分页')]
public function page(): JsonResponse
{
return $this->_page($this->exampleService);
}
/**
* 查询列表
*
* 对应权限:sys:example:list,操作日志类型:查询。
* 返回不分页的完整列表,适合下拉选择等场景。
*
* @return JsonResponse 列表数据
*/
#[OperationLog('示例管理-查询列表', OperationLog::TYPE_QUERY)]
#[Permission('sys:example:list', '示例列表')]
public function list(): JsonResponse
{
return $this->_list($this->exampleService);
}
/**
* 查询详情
*
* 对应权限:sys:example:detail,操作日志类型:查询,
* 日志模板中包含示例ID:{id}。
*
* @param int $id 示例ID
* @return JsonResponse 示例详情
*/
#[OperationLog('示例管理-查询详情', OperationLog::TYPE_QUERY, '查询示例ID:{id}')]
#[Permission('sys:example:detail', '示例详情')]
public function detail(int $id): JsonResponse
{
return $this->_detail($this->exampleService, $id);
}
/**
* 新增
*
* 对应权限:sys:example:add,操作日志类型:新增,
* 日志模板中包含示例名称:{name}。
*
* @param ExampleRequest $request 已通过校验的示例请求
* @return JsonResponse 新增结果
*/
#[OperationLog('示例管理-新增', OperationLog::TYPE_ADD, '新增示例:{name}')]
#[Permission('sys:example:add', '添加示例')]
public function add(ExampleRequest $request): JsonResponse
{
return $this->_add($this->exampleService, $request->validated());
}
/**
* 修改
*
* 对应权限:sys:example:update,操作日志类型:修改。
* 若请求数据中缺少 id,则直接返回失败响应。
*
* @param ExampleRequest $request 已通过校验的示例请求
* @return JsonResponse 修改结果
*/
#[OperationLog('示例管理-修改', OperationLog::TYPE_UPDATE, '修改示例ID:{id}')]
#[Permission('sys:example:update', '修改示例')]
public function update(ExampleRequest $request): JsonResponse
{
$data = $request->validated();
$id = $data['id'] ?? 0;
if (empty($id)) {
return $this->fail('缺少id参数');
}
return $this->_update($this->exampleService, $id, $data);
}
/**
* 删除
*
* 对应权限:sys:example:delete,操作日志类型:删除,
* 日志模板中包含示例ID:{id}。
*
* @param int $id 示例ID
* @return JsonResponse 删除结果
*/
#[OperationLog('示例管理-删除', OperationLog::TYPE_DELETE, '删除示例ID:{id}')]
#[Permission('sys:example:delete', '删除示例')]
public function delete(int $id): JsonResponse
{
return $this->_delete($this->exampleService, $id);
}
/**
* 批量删除
*
* 对应权限:sys:example:batchDelete,操作日志类型:删除。
* 从 JSON 请求体中读取 ids 数组,未传时按空数组处理。
*
* @return JsonResponse 批量删除结果
*/
#[OperationLog('示例管理-批量删除', OperationLog::TYPE_DELETE)]
#[Permission('sys:example:batchDelete', '批量删除')]
public function batchDelete(): JsonResponse
{
$ids = $this->getJsonBody()['ids'] ?? [];
return $this->_batchDelete($this->exampleService, $ids);
}
/**
* 导出数据
*
* 对应权限:sys:example:export,操作日志类型:导出(TYPE_EXPORT)。
* 由基类 _export() 统一处理文件生成与响应封装。
*
* @return JsonResponse 导出结果(data 为文件地址)
*/
#[OperationLog('示例管理-导出数据', OperationLog::TYPE_EXPORT)]
#[Permission('sys:example:export', '导出示例')]
public function export(): JsonResponse
{
return $this->_export($this->exampleService);
}
/**
* 导入数据
*
* 对应权限:sys:example:import,操作日志类型:导入(TYPE_IMPORT)。
* 由基类 _import() 统一处理文件校验(格式/大小/MIME)与响应封装。
*
* @return JsonResponse 导入结果
*/
#[OperationLog('示例管理-导入数据', OperationLog::TYPE_IMPORT, '通过Excel导入示例数据')]
#[Permission('sys:example:import', '导入示例')]
public function import(): JsonResponse
{
return $this->_import($this->exampleService);
}
}
```
:::tip 控制器模式
每个控制器遵循固定模式:
1. **构造函数注入 Service**(Laravel 依赖注入,`protected` 简写属性即成员属性)
2. 每个方法标注 `#[OperationLog]` 和 `#[Permission]` 注解
3. 简单调用 `$this->_page()` / `_detail()` / `_add()` 等快捷方法
4. 入参由 FormRequest 类型注入完成格式校验(见 [3.4 FormRequest 验证器](/zh/guide/validator.html))
:::
## Controller 基类快捷方法
`app/Http/Controllers/Controller.php` 提供的通用方法:
| 方法 | 用途 | 内部调用 | 失败场景 |
|------|------|----------|----------|
| `_page($service)` | 分页查询 | `Service::pageList()` | - |
| `_list($service)` | 全量列表 | `Service::allList()` | - |
| `_treeList($service)` | 树形列表 | `Service::treeList()` | - |
| `_detail($service, $id)` | 详情查询 | `Service::detail($id)` | 空数据返回 `code=404` |
| `_add($service, $data)` | 新增 | `Service::add($data)` | 捕获 `BizException` → `fail(消息, code)` |
| `_update($service, $id, $data)` | 修改 | `Service::update($id, $data)` | id 缺失提示;捕获 `BizException` |
| `_delete($service, $id)` | 删除 | `Service::delete($id)` | 捕获 `BizException`;其他异常记日志后返回通用提示 |
| `_batchDelete($service, $ids)` | 批量删除 | `Service::batchDelete($ids)` | ids 为空提示;同上 |
| `_export($service)` | 导出 | `Service::export()` | 捕获 `BizException`(如"无数据可导出") |
| `_import($service)` | 导入 | `Service::import()` | 文件格式/大小/MIME 校验 + 通用提示 |
响应封装方法:`$this->success($data, $msg)` / `$this->fail($msg, $code)` / `$this->paginate($result)`(委托给 `Support\Result`,见 [5.16 统一响应与异常抛出](/zh/core/exception.html))。
参数获取方法:`getPageParams()`(pageNo/pageSize,限 1~100)、`getJsonBody()`(JSON 请求体)、`getParams()`(query + body 合并)。
## 注解说明
### #[OperationLog] 操作日志注解
```php
#[OperationLog('示例管理-新增', OperationLog::TYPE_ADD, '新增示例:{name}')]
// │ │ │
// │ │ └── 描述模板(支持 {param} 占位符,替换为请求参数值)
// │ └── 操作类型常量(TYPE_QUERY/ADD/UPDATE/DELETE/IMPORT/EXPORT 等 30 种)
// └── 操作标题
```
- 由 `log.operation` 中间件采集,响应后(terminable 阶段)落库;
- 写操作(POST/PUT/DELETE)始终记录,读操作按注解类型选择性记录。
### #[Permission] 权限注解
```php
#[Permission('sys:example:add', '添加示例')]
// │ │
// │ └── 权限名称(菜单管理中显示)
// └── 权限编码(菜单表 permission 字段)
```
- 由 `auth.jwt` 中间件反射校验:解析注解 → 比对当前用户权限列表 → 无权限返回 `code=403`;
- 超级管理员(用户 ID=1)直接放行;
- 未标注 `#[Permission]` 的认证接口只需登录即可访问。
## 与手动构造响应的对比
```php
// ✅ 推荐:快捷方法(异常处理、日志、404 兜底全部内置)
public function page(): JsonResponse
{
return $this->_page($this->exampleService);
}
// 特殊场景:手动组织响应
public function stats(): JsonResponse
{
$data = $this->exampleService->stats();
if (! $data) {
return $this->fail('暂无统计数据', 404);
}
return $this->success($data);
}
```