# 4.7 横切机制 :::tip 概述 横切机制是 BaseLogic/BaseModel 内置的自动处理能力,开发者通过属性配置启用,无需手动编写处理代码。项目共有 9 种横切机制,覆盖数据转换、文件处理、校验、权限、隔离等维度。 ::: ## 横切机制总览 | 机制 | 配置位置 | 配置属性 | 触发时机 | |------|---------|---------|---------| | 驼峰/下划线转换 | `config/api.php` | `camel_snake_convert` | 请求入、响应出 | | 文件字段处理 | Logic | `fileFields` + `fileSaveDir` | add/update 写入,detail/page 读取 | | 多文件字段处理 | Logic | `multiFileFields` + `fileSaveDir` | add/update 写入,detail/page 读取 | | 富文本占位符 | Logic | `contentFields` | add/update 写入,detail/page 读取 | | 枚举显示名翻译 | Logic | `serializeMaps` | detail/page 查询后 | | 自动参数校验 | Logic | `validateClass` | add/update 前 | | 唯一性校验 | Logic | `uniqueFields` | add/update 时 | | 数据权限过滤 | Logic | `dataScopeUserField` / `dataScopeDeptField` | 查询时 | | 租户隔离 | Logic(继承 BaseTenantLogic) | `tenantScopeField` | 查询时 + 新增时 | | 审计字段 + 软删除 | BaseModel | 自动生效 | 所有 CRUD | ## 1. 驼峰/下划线自动转换 前端使用 camelCase,数据库使用 snake_case,框架自动完成双向转换。 ### 转换流程 ```text 请求方向(前端 → 数据库): 前端传参:{ userName: "admin", createTime: "2026-01-01" } │ ▼ BaseLogic::convertQueryParams() │ array_camel_to_snake($params, $except) ▼ 数据库操作:WHERE user_name = 'admin' AND create_time = '2026-01-01' 响应方向(数据库 → 前端): 数据库数据:{ user_name: "admin", create_time: "2026-01-01" } │ ▼ Result::convertData() │ array_snake_to_camel($data) ▼ 前端接收:{ userName: "admin", createTime: "2026-01-01" } ``` ### 排除项(不参与转换) 以下参数保持 camelCase,不转为 snake_case: - `pageNo`、`pageSize` — 分页参数 - `orderField`、`orderType` — 排序参数 - `fields` — 字段过滤参数 ### 配置开关 ```php // config/api.php 'camel_snake_convert' => env('api.camel_snake_convert', true), ``` ```ini # .env API.CAMEL_SNAKE_CONVERT = true # 开启(默认) API.CAMEL_SNAKE_CONVERT = false # 关闭(请求保持驼峰,响应保持下划线) ``` :::tip 开发者无需手动处理 驼峰/下划线转换完全自动,开发者只需在前端用 camelCase,在数据库用 snake_case,框架自动处理双向转换。 ::: ## 2. 文件字段自动处理 **配置方式**:Logic 中设置 `fileFields` 和 `fileSaveDir` ```php class ArticleLogic extends BaseTenantLogic { protected array $fileFields = ['cover', 'attachment']; protected string $fileSaveDir = 'article'; } ``` ### 写入时(add/update) ```text 前端传入:cover = "https://temp.example.com/tmp/abc123.jpg" │ ▼ processFileFieldsOnSave() │ 1. 检测字段值是否为临时文件 URL │ 2. 调用 save_file() 将临时文件迁移到正式目录 │ 3. 去掉域名,只存储相对路径 ▼ 入库存储:cover = "/uploads/article/20260922/abc123.jpg" ``` ### 读取时(detail/pageList/allList) ```text 数据库读取:cover = "/uploads/article/20260922/abc123.jpg" │ ▼ processFileFieldsOnRead() │ 调用 get_file_url() 补全域名 ▼ 前端接收:cover = "https://cdn.example.com/uploads/article/20260922/abc123.jpg" ``` ### 多个单文件字段 ```php protected array $fileFields = ['cover', 'avatar', 'attachment']; // 三个字段都会自动处理 ``` ## 3. 多文件字段自动处理 当一个字段存储多个文件URL(逗号分隔)时,使用 `multiFileFields`: **配置方式**:Logic 中设置 `multiFileFields`(复用 `fileSaveDir`) ```php class ArticleLogic extends BaseTenantLogic { protected array $multiFileFields = ['images', 'attachments']; protected string $fileSaveDir = 'article'; } ``` **写入时(add/update)** ```text 前端传入:images = "http://example.com/temp/a.png,http://example.com/temp/b.png" │ ▼ processMultiFileFieldsOnSave() │ 1. 按逗号拆分为数组 │ 2. 逐个调用 save_file() 迁移临时文件并去掉域名 │ 3. 重新用逗号拼接 ▼ 入库存储:images = "article/20260922/a.png,article/20260922/b.png" ``` **读取时(detail/pageList/allList)** ```text 数据库读取:images = "article/20260922/a.png,article/20260922/b.png" │ ▼ processMultiFileFieldsOnRead() │ 1. 按逗号拆分为数组 │ 2. 逐个调用 get_file_url() 补全域名 │ 3. 重新用逗号拼接 ▼ 前端接收:images = "http://cdn.example.com/article/20260922/a.png,http://cdn.example.com/article/20260922/b.png" ``` ## 4. 富文本占位符机制 **配置方式**:Logic 中设置 `contentFields` ```php class ArticleLogic extends BaseTenantLogic { protected array $contentFields = ['content']; } ``` ### 写入时 ```text 前端传入:content = '

文章内容

' │ ▼ processContentFieldsOnSave() │ 1. 解析 HTML 中的 /