签入版本
This commit is contained in:
@@ -0,0 +1,343 @@
|
||||
# 5.18 注解系统总览
|
||||
|
||||
:::tip 概述
|
||||
项目使用 PHP 8 原生注解(Attribute)实现声明式配置,目前有三个注解类,由 AttributeService 通过反射读取。注解标注在控制器方法或类上,中间件在请求处理过程中读取并执行对应逻辑(日志记录、权限校验、演示放行)。
|
||||
:::
|
||||
|
||||
## 注解清单
|
||||
|
||||
| 注解 | 作用域 | 读取方 | 用途 | 必填参数 |
|
||||
|------|--------|--------|------|---------|
|
||||
| `#[Log]` | 方法 | LogMiddleware | 操作日志记录 | title |
|
||||
| `#[Permission]` | 方法/类 | AuthMiddleware | 权限校验 | code |
|
||||
| `#[DemoAllow]` | 方法 | DemoMiddleware | 演示环境放行 | 无 |
|
||||
|
||||
## 注解执行顺序
|
||||
|
||||
```text
|
||||
请求进入
|
||||
│
|
||||
▼
|
||||
CorsMiddleware(全局)
|
||||
│
|
||||
▼
|
||||
AuthMiddleware
|
||||
│ 读取 #[Permission] 注解
|
||||
│ 校验权限码 → 无权限返回 403
|
||||
│
|
||||
▼
|
||||
TenantMiddleware
|
||||
│
|
||||
▼
|
||||
DemoMiddleware
|
||||
│ 读取 #[DemoAllow] 注解
|
||||
│ 演示环境 + 写操作 + 无注解 → 拦截返回 403
|
||||
│
|
||||
▼
|
||||
LogMiddleware
|
||||
│ 读取 #[Log] 注解
|
||||
│ 记录操作日志
|
||||
│
|
||||
▼
|
||||
Controller 方法执行
|
||||
```
|
||||
|
||||
## #[Log] 操作日志注解
|
||||
|
||||
**文件:** `app/attribute/Log.php`
|
||||
**读取方:** LogMiddleware
|
||||
**作用域:** 方法
|
||||
|
||||
### 基本用法
|
||||
|
||||
```php
|
||||
#[Log('用户管理-新增记录', Log::TYPE_ADD, '新增用户:{username}')]
|
||||
// │ │ │
|
||||
// │ │ └── 描述(支持 {param} 占位符)
|
||||
// │ └── 操作类型(40种常量)
|
||||
// └── 操作标题
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `title` | string | ✓ | 操作标题,如"用户管理-新增记录" |
|
||||
| `type` | int | ✗ | 操作类型常量,默认 0(由中间件按 HTTP 方法推断) |
|
||||
| `description` | string | ✗ | 详细描述,支持 `{param}` 占位符 |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```php
|
||||
// 新增
|
||||
#[Log('用户管理-新增记录', Log::TYPE_ADD, '新增用户:{username}')]
|
||||
|
||||
// 修改
|
||||
#[Log('用户管理-修改记录', Log::TYPE_UPDATE, '修改用户ID:{id}')]
|
||||
|
||||
// 删除
|
||||
#[Log('用户管理-删除记录', Log::TYPE_DELETE, '删除用户ID:{id}')]
|
||||
|
||||
// 批量删除
|
||||
#[Log('用户管理-批量删除记录', Log::TYPE_DELETE)]
|
||||
|
||||
// 导出(GET 请求也会记录)
|
||||
#[Log('用户管理-导出数据', Log::TYPE_EXPORT)]
|
||||
|
||||
// 导入
|
||||
#[Log('用户管理-导入数据', Log::TYPE_IMPORT, '通过Excel导入用户数据')]
|
||||
|
||||
// 重置密码
|
||||
#[Log('用户管理-重置密码', Log::TYPE_RESET, '重置用户ID:{id}的密码')]
|
||||
|
||||
// 登录(TYPE_LOGIN)
|
||||
#[Log('系统登录-用户登录', Log::TYPE_LOGIN)]
|
||||
|
||||
// 登出(TYPE_LOGOUT)
|
||||
#[Log('系统登录-用户登出', Log::TYPE_LOGOUT)]
|
||||
|
||||
// 查询(GET 默认不记录,除非是导出/下载/导入类型)
|
||||
#[Log('用户管理-查询分页记录', Log::TYPE_QUERY)]
|
||||
```
|
||||
|
||||
### 占位符替换
|
||||
|
||||
```php
|
||||
#[Log('文章管理-删除记录', Log::TYPE_DELETE, '删除文章ID:{id}')]
|
||||
|
||||
// {id} 会被替换为 $request->param('id') 的值
|
||||
// 实际记录:"删除文章ID:42"
|
||||
```
|
||||
|
||||
### 操作类型常量
|
||||
|
||||
| 常量 | 值 | 说明 | GET 是否记录 |
|
||||
|------|-----|------|-------------|
|
||||
| `TYPE_ADD` | 1 | 新增 | — |
|
||||
| `TYPE_UPDATE` | 2 | 修改 | — |
|
||||
| `TYPE_DELETE` | 3 | 删除 | — |
|
||||
| `TYPE_QUERY` | 4 | 查询 | ✗ |
|
||||
| `TYPE_IMPORT` | 5 | 导入 | ✓ |
|
||||
| `TYPE_EXPORT` | 6 | 导出 | ✓ |
|
||||
| `TYPE_DOWNLOAD` | 7 | 下载 | ✓ |
|
||||
| `TYPE_APPROVE` | 8 | 审批 | — |
|
||||
| `TYPE_REJECT` | 9 | 驳回 | — |
|
||||
| `TYPE_SUBMIT` | 10 | 提交 | — |
|
||||
| `TYPE_WITHDRAW` | 11 | 撤回 | — |
|
||||
| `TYPE_LOGIN` | 21 | 登录 | — |
|
||||
| `TYPE_LOGOUT` | 22 | 登出 | — |
|
||||
| `TYPE_RESET` | 24 | 重置 | — |
|
||||
| `TYPE_OTHER` | 99 | 其他 | — |
|
||||
|
||||
完整常量列表见 `app/attribute/Log.php`(共 40 种)。
|
||||
|
||||
## #[Permission] 权限注解
|
||||
|
||||
**文件:** `app/attribute/Permission.php`
|
||||
**读取方:** AuthMiddleware
|
||||
**作用域:** 方法 + 类(方法优先)
|
||||
|
||||
### 基本用法
|
||||
|
||||
```php
|
||||
#[Permission('sys:user:add', '添加用户')]
|
||||
// │ │
|
||||
// │ └── 权限名称(用于提示和日志)
|
||||
// └── 权限编码
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `code` | string | ✓ | 权限编码,如 `sys:user:add` |
|
||||
| `name` | string | ✗ | 权限名称,用于提示和日志 |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```php
|
||||
// 方法级注解(最常用)
|
||||
#[Permission('sys:user:add', '添加用户')]
|
||||
public function add(): Json { ... }
|
||||
|
||||
#[Permission('sys:user:page', '用户分页')]
|
||||
public function page(): Json { ... }
|
||||
|
||||
// 类级注解(该类所有方法都需此权限)
|
||||
#[Permission('sys:user:manage', '用户管理')]
|
||||
class UserController extends BaseController { ... }
|
||||
```
|
||||
|
||||
### 优先级规则
|
||||
|
||||
```text
|
||||
1. 方法上有 #[Permission] → 使用方法的权限码
|
||||
2. 方法上无 #[Permission],类上有 → 使用类的权限码
|
||||
3. 方法和类都没有 → 跳过权限校验(所有已登录用户可访问)
|
||||
```
|
||||
|
||||
### 超级管理员
|
||||
|
||||
```text
|
||||
uid=1 的用户为超级管理员,跳过所有权限校验。
|
||||
AuthMiddleware 检测到 uid=1 时直接放行,不查询权限表。
|
||||
```
|
||||
|
||||
## #[DemoAllow] 演示放行注解
|
||||
|
||||
**文件:** `app/attribute/DemoAllow.php`
|
||||
**读取方:** DemoMiddleware
|
||||
**作用域:** 方法
|
||||
**参数:** 无
|
||||
|
||||
### 基本用法
|
||||
|
||||
```php
|
||||
// 标注后,演示环境下该写操作不受拦截
|
||||
#[DemoAllow]
|
||||
public function refreshCache(): Json { ... }
|
||||
|
||||
#[DemoAllow]
|
||||
public function login(): Json { ... }
|
||||
```
|
||||
|
||||
### 工作原理
|
||||
|
||||
```text
|
||||
DemoMiddleware 检查逻辑:
|
||||
1. APP_DEMO = false → 放行(非演示环境,env('app_demo'))
|
||||
2. APP_DEMO = true + GET 请求 → 放行
|
||||
3. APP_DEMO = true + 写操作(POST/PUT/DELETE)
|
||||
├─ 方法有 #[DemoAllow] → 放行
|
||||
└─ 方法无 #[DemoAllow] → 拦截,返回 403
|
||||
```
|
||||
|
||||
### 典型场景
|
||||
|
||||
```php
|
||||
// 登录接口:演示环境也需要允许登录
|
||||
#[DemoAllow]
|
||||
public function login(): Json { ... }
|
||||
|
||||
// 缓存刷新:演示环境也需要允许
|
||||
#[DemoAllow]
|
||||
public function refreshCache(): Json { ... }
|
||||
|
||||
// 用户新增:演示环境禁止(无 #[DemoAllow])
|
||||
public function add(): Json { ... }
|
||||
```
|
||||
|
||||
## 注解组合使用
|
||||
|
||||
```php
|
||||
// 操作日志 + 权限校验(最常见组合)
|
||||
#[Log('用户管理-新增记录', Log::TYPE_ADD, '新增用户:{username}')]
|
||||
#[Permission('sys:user:add', '添加用户')]
|
||||
public function add(): Json { ... }
|
||||
|
||||
// 操作日志 + 权限校验 + 演示放行
|
||||
#[Log('系统登录-用户登录', Log::TYPE_LOGIN)]
|
||||
#[Permission('sys:login:do', '登录')]
|
||||
#[DemoAllow]
|
||||
public function login(): Json { ... }
|
||||
|
||||
// 仅权限校验(查询接口不需要日志)
|
||||
#[Permission('sys:user:page', '用户分页')]
|
||||
public function page(): Json { ... }
|
||||
|
||||
// 仅日志(不需要权限校验的公开接口)
|
||||
#[Log('系统登录-获取验证码', Log::TYPE_OTHER)]
|
||||
public function captcha(): Json { ... }
|
||||
```
|
||||
|
||||
## AttributeService
|
||||
|
||||
**文件:** `app/service/AttributeService.php`
|
||||
**职责:** 通过反射读取注解,带缓存机制
|
||||
|
||||
### 方法清单
|
||||
|
||||
| 方法 | 说明 | 返回值 |
|
||||
|------|------|--------|
|
||||
| `getLog($class, $method)` | 获取方法上的 Log 注解 | `Log\|null` |
|
||||
| `getPermission($class, $method)` | 获取方法/类上的 Permission 注解 | `Permission\|null` |
|
||||
| `hasDemoAllow($class, $method)` | 判断方法是否有 DemoAllow 注解 | `bool` |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```php
|
||||
// 获取方法上的 Log 注解
|
||||
$log = AttributeService::getLog('app\controller\UserController', 'add');
|
||||
if ($log) {
|
||||
$title = $log->title; // '用户管理-新增记录'
|
||||
$type = $log->type; // 1 (TYPE_ADD)
|
||||
$desc = $log->description; // '新增用户:{username}'
|
||||
}
|
||||
|
||||
// 获取方法上的 Permission 注解(方法优先,其次类)
|
||||
$perm = AttributeService::getPermission('app\controller\UserController', 'add');
|
||||
if ($perm) {
|
||||
$code = $perm->code; // 'sys:user:add'
|
||||
$name = $perm->name; // '添加用户'
|
||||
}
|
||||
|
||||
// 判断是否有 DemoAllow 注解
|
||||
$allowed = AttributeService::hasDemoAllow('app\controller\DictController', 'refreshCache');
|
||||
// true / false
|
||||
```
|
||||
|
||||
### 缓存机制
|
||||
|
||||
```text
|
||||
缓存键:类名@方法名
|
||||
缓存位置:static::$localCache(请求级缓存)
|
||||
缓存策略:同一请求内不重复反射,跨请求重新解析
|
||||
|
||||
示例:
|
||||
"app\controller\UserController@add" → Log 实例 + Permission 实例
|
||||
```
|
||||
|
||||
## 创建自定义注解
|
||||
|
||||
```php
|
||||
// 1. 定义注解类
|
||||
// app/attribute/RateLimit.php
|
||||
namespace app\attribute;
|
||||
|
||||
use Attribute;
|
||||
|
||||
#[Attribute(Attribute::TARGET_METHOD)]
|
||||
class RateLimit
|
||||
{
|
||||
public function __construct(
|
||||
public int $max = 100, // 最大请求数
|
||||
public int $window = 60, // 窗口期(秒)
|
||||
) {}
|
||||
}
|
||||
|
||||
// 2. 在控制器方法上使用
|
||||
#[RateLimit(max: 10, window: 60)]
|
||||
public function sendSms(): Json { ... }
|
||||
|
||||
// 3. 在中间件中读取
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
$ref = new \ReflectionMethod($controller, $method);
|
||||
$attrs = $ref->getAttributes(RateLimit::class);
|
||||
if (!empty($attrs)) {
|
||||
$rateLimit = $attrs[0]->newInstance();
|
||||
// $rateLimit->max, $rateLimit->window
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
| 实践 | 说明 |
|
||||
|------|------|
|
||||
| 注解即文档 | 通过注解即可了解接口的操作类型、权限要求 |
|
||||
| 写操作必须标注 `#[Log]` | 删除、重置密码等敏感操作必须记录日志 |
|
||||
| 权限码统一格式 | 使用 `sys:{module}:{action}` 格式 |
|
||||
| 描述加关键参数 | 用 `{param}` 占位符记录业务主键,便于追溯 |
|
||||
| 查询不标注 `#[Log]` | 普通查询不记录日志,避免日志膨胀 |
|
||||
| 组合使用 | `#[Log]` + `#[Permission]` 组合是最常见的模式 |
|
||||
Reference in New Issue
Block a user