241 lines
78 KiB
JavaScript
241 lines
78 KiB
JavaScript
import{_ as i,o as a,c as n,a2 as t}from"./chunks/framework.B-QBKwc1.js";const o=JSON.parse('{"title":"横切机制","description":"横切机制是 BaseLogic/BaseModel 内置的自动处理能力,开发者通过属性配置启用,无需手动编写处理代码。","frontmatter":{},"headers":[],"relativePath":"zh/architecture/cross-cutting.md","filePath":"zh/architecture/cross-cutting.md"}'),p={name:"zh/architecture/cross-cutting.md"};function l(e,s,h,k,d,r){return a(),n("div",null,[...s[0]||(s[0]=[t(`<h1 id="_4-7-横切机制" tabindex="-1">4.7 横切机制 <a class="header-anchor" href="#_4-7-横切机制" aria-label="Permalink to "4.7 横切机制""></a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>横切机制是 BaseLogic/BaseModel 内置的自动处理能力,开发者通过属性配置启用,无需手动编写处理代码。项目共有 9 种横切机制,覆盖数据转换、文件处理、校验、权限、隔离等维度。</p></div><h2 id="横切机制总览" tabindex="-1">横切机制总览 <a class="header-anchor" href="#横切机制总览" aria-label="Permalink to "横切机制总览""></a></h2><table tabindex="0"><thead><tr><th>机制</th><th>配置位置</th><th>配置属性</th><th>触发时机</th></tr></thead><tbody><tr><td>驼峰/下划线转换</td><td><code>config/api.php</code></td><td><code>camel_snake_convert</code></td><td>请求入、响应出</td></tr><tr><td>文件字段处理</td><td>Logic</td><td><code>fileFields</code> + <code>fileSaveDir</code></td><td>add/update 写入,detail/page 读取</td></tr><tr><td>多文件字段处理</td><td>Logic</td><td><code>multiFileFields</code> + <code>fileSaveDir</code></td><td>add/update 写入,detail/page 读取</td></tr><tr><td>富文本占位符</td><td>Logic</td><td><code>contentFields</code></td><td>add/update 写入,detail/page 读取</td></tr><tr><td>枚举显示名翻译</td><td>Logic</td><td><code>serializeMaps</code></td><td>detail/page 查询后</td></tr><tr><td>自动参数校验</td><td>Logic</td><td><code>validateClass</code></td><td>add/update 前</td></tr><tr><td>唯一性校验</td><td>Logic</td><td><code>uniqueFields</code></td><td>add/update 时</td></tr><tr><td>数据权限过滤</td><td>Logic</td><td><code>dataScopeUserField</code> / <code>dataScopeDeptField</code></td><td>查询时</td></tr><tr><td>租户隔离</td><td>Logic(继承 BaseTenantLogic)</td><td><code>tenantScopeField</code></td><td>查询时 + 新增时</td></tr><tr><td>审计字段 + 软删除</td><td>BaseModel</td><td>自动生效</td><td>所有 CRUD</td></tr></tbody></table><h2 id="_1-驼峰-下划线自动转换" tabindex="-1">1. 驼峰/下划线自动转换 <a class="header-anchor" href="#_1-驼峰-下划线自动转换" aria-label="Permalink to "1. 驼峰/下划线自动转换""></a></h2><p>前端使用 camelCase,数据库使用 snake_case,框架自动完成双向转换。</p><h3 id="转换流程" tabindex="-1">转换流程 <a class="header-anchor" href="#转换流程" aria-label="Permalink to "转换流程""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>请求方向(前端 → 数据库):</span></span>
|
||
<span class="line"><span> 前端传参:{ userName: "admin", createTime: "2026-01-01" }</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ BaseLogic::convertQueryParams()</span></span>
|
||
<span class="line"><span> │ array_camel_to_snake($params, $except)</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span> 数据库操作:WHERE user_name = 'admin' AND create_time = '2026-01-01'</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>响应方向(数据库 → 前端):</span></span>
|
||
<span class="line"><span> 数据库数据:{ user_name: "admin", create_time: "2026-01-01" }</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ Result::convertData()</span></span>
|
||
<span class="line"><span> │ array_snake_to_camel($data)</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span> 前端接收:{ userName: "admin", createTime: "2026-01-01" }</span></span></code></pre></div><h3 id="排除项-不参与转换-" tabindex="-1">排除项(不参与转换) <a class="header-anchor" href="#排除项-不参与转换-" aria-label="Permalink to "排除项(不参与转换)""></a></h3><p>以下参数保持 camelCase,不转为 snake_case:</p><ul><li><code>pageNo</code>、<code>pageSize</code> — 分页参数</li><li><code>orderField</code>、<code>orderType</code> — 排序参数</li><li><code>fields</code> — 字段过滤参数</li></ul><h3 id="配置开关" tabindex="-1">配置开关 <a class="header-anchor" href="#配置开关" aria-label="Permalink to "配置开关""></a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// config/api.php</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">camel_snake_convert</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> env</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">api.camel_snake_convert</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> true</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">),</span></span></code></pre></div><div class="language-ini vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># .env</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">API.CAMEL_SNAKE_CONVERT</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> true </span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 开启(默认)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">API.CAMEL_SNAKE_CONVERT</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> false </span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 关闭(请求保持驼峰,响应保持下划线)</span></span></code></pre></div><div class="tip custom-block"><p class="custom-block-title">开发者无需手动处理</p><p>驼峰/下划线转换完全自动,开发者只需在前端用 camelCase,在数据库用 snake_case,框架自动处理双向转换。</p></div><h2 id="_2-文件字段自动处理" tabindex="-1">2. 文件字段自动处理 <a class="header-anchor" href="#_2-文件字段自动处理" aria-label="Permalink to "2. 文件字段自动处理""></a></h2><p><strong>配置方式</strong>:Logic 中设置 <code>fileFields</code> 和 <code>fileSaveDir</code></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">cover</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">attachment</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileSaveDir</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">article</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="写入时-add-update-" tabindex="-1">写入时(add/update) <a class="header-anchor" href="#写入时-add-update-" aria-label="Permalink to "写入时(add/update)""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>前端传入:cover = "https://temp.example.com/tmp/abc123.jpg"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ processFileFieldsOnSave()</span></span>
|
||
<span class="line"><span> │ 1. 检测字段值是否为临时文件 URL</span></span>
|
||
<span class="line"><span> │ 2. 调用 save_file() 将临时文件迁移到正式目录</span></span>
|
||
<span class="line"><span> │ 3. 去掉域名,只存储相对路径</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>入库存储:cover = "/uploads/article/20260922/abc123.jpg"</span></span></code></pre></div><h3 id="读取时-detail-pageList-allList-" tabindex="-1">读取时(detail/pageList/allList) <a class="header-anchor" href="#读取时-detail-pageList-allList-" aria-label="Permalink to "读取时(detail/pageList/allList)""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>数据库读取:cover = "/uploads/article/20260922/abc123.jpg"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ processFileFieldsOnRead()</span></span>
|
||
<span class="line"><span> │ 调用 get_file_url() 补全域名</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>前端接收:cover = "https://cdn.example.com/uploads/article/20260922/abc123.jpg"</span></span></code></pre></div><h3 id="多个单文件字段" tabindex="-1">多个单文件字段 <a class="header-anchor" href="#多个单文件字段" aria-label="Permalink to "多个单文件字段""></a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">cover</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">avatar</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">attachment</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 三个字段都会自动处理</span></span></code></pre></div><h2 id="_3-多文件字段自动处理" tabindex="-1">3. 多文件字段自动处理 <a class="header-anchor" href="#_3-多文件字段自动处理" aria-label="Permalink to "3. 多文件字段自动处理""></a></h2><p>当一个字段存储多个文件URL(逗号分隔)时,使用 <code>multiFileFields</code>:</p><p><strong>配置方式</strong>:Logic 中设置 <code>multiFileFields</code>(复用 <code>fileSaveDir</code>)</p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">multiFileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">images</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">attachments</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileSaveDir</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">article</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><p><strong>写入时(add/update)</strong></p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>前端传入:images = "http://example.com/temp/a.png,http://example.com/temp/b.png"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ processMultiFileFieldsOnSave()</span></span>
|
||
<span class="line"><span> │ 1. 按逗号拆分为数组</span></span>
|
||
<span class="line"><span> │ 2. 逐个调用 save_file() 迁移临时文件并去掉域名</span></span>
|
||
<span class="line"><span> │ 3. 重新用逗号拼接</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>入库存储:images = "article/20260922/a.png,article/20260922/b.png"</span></span></code></pre></div><p><strong>读取时(detail/pageList/allList)</strong></p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>数据库读取:images = "article/20260922/a.png,article/20260922/b.png"</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ processMultiFileFieldsOnRead()</span></span>
|
||
<span class="line"><span> │ 1. 按逗号拆分为数组</span></span>
|
||
<span class="line"><span> │ 2. 逐个调用 get_file_url() 补全域名</span></span>
|
||
<span class="line"><span> │ 3. 重新用逗号拼接</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>前端接收:images = "http://cdn.example.com/article/20260922/a.png,http://cdn.example.com/article/20260922/b.png"</span></span></code></pre></div><h2 id="_4-富文本占位符机制" tabindex="-1">4. 富文本占位符机制 <a class="header-anchor" href="#_4-富文本占位符机制" aria-label="Permalink to "4. 富文本占位符机制""></a></h2><p><strong>配置方式</strong>:Logic 中设置 <code>contentFields</code></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">contentFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">content</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="写入时" tabindex="-1">写入时 <a class="header-anchor" href="#写入时" aria-label="Permalink to "写入时""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>前端传入:content = '<p>文章内容</p><img src="https://temp.example.com/tmp/img001.jpg">'</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ processContentFieldsOnSave()</span></span>
|
||
<span class="line"><span> │ 1. 解析 HTML 中的 <img>/<video>/<a> 标签</span></span>
|
||
<span class="line"><span> │ 2. 将临时文件迁移到正式目录</span></span>
|
||
<span class="line"><span> │ 3. 替换为 [IMG_URL]/相对路径 占位符</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>入库存储:content = '<p>文章内容</p><img src="[IMG_URL]/uploads/article/img001.jpg">'</span></span></code></pre></div><h3 id="读取时" tabindex="-1">读取时 <a class="header-anchor" href="#读取时" aria-label="Permalink to "读取时""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>数据库读取:content = '<p>文章内容</p><img src="[IMG_URL]/uploads/article/img001.jpg">'</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ processContentFieldsOnRead()</span></span>
|
||
<span class="line"><span> │ 将 [IMG_URL] 替换为配置的实际域名</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>前端接收:content = '<p>文章内容</p><img src="https://cdn.example.com/uploads/article/img001.jpg">'</span></span></code></pre></div><h3 id="好处" tabindex="-1">好处 <a class="header-anchor" href="#好处" aria-label="Permalink to "好处""></a></h3><p>域名变更时只需修改 <code>config/file.php</code> 中的 <code>domain_url</code>,已存储的内容无需逐条更新。</p><h2 id="_5-枚举显示名翻译" tabindex="-1">5. 枚举显示名翻译 <a class="header-anchor" href="#_5-枚举显示名翻译" aria-label="Permalink to "5. 枚举显示名翻译""></a></h2><p><strong>配置方式</strong>:Logic 中设置 <code>serializeMaps</code></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 枚举显示名映射</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">serializeMaps</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">article_status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">type</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">article_type</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> ];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="工作原理" tabindex="-1">工作原理 <a class="header-anchor" href="#工作原理" aria-label="Permalink to "工作原理""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>数据库查询:{ status: 1, type: 0 }</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ processSerializeMaps()</span></span>
|
||
<span class="line"><span> │ DictService::getText('article_status', 1) → '已发布'</span></span>
|
||
<span class="line"><span> │ DictService::getText('article_type', 0) → '原创'</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>前端接收:{ status: 1, statusText: '已发布', type: 0, typeText: '原创' }</span></span></code></pre></div><h3 id="字段名映射" tabindex="-1">字段名映射 <a class="header-anchor" href="#字段名映射" aria-label="Permalink to "字段名映射""></a></h3><p>当字典编码与字段名不同时,可指定映射:</p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">/**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 枚举显示名映射</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">serializeMaps</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">gender</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">gender</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">user_status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span></code></pre></div><h2 id="_6-自动参数校验" tabindex="-1">6. 自动参数校验 <a class="header-anchor" href="#_6-自动参数校验" aria-label="Permalink to "6. 自动参数校验""></a></h2><p><strong>配置方式</strong>:Logic 中设置 <code>validateClass</code></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">validateClass</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F286C4;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;"> \\</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">app</span><span style="--shiki-dark:#F286C4;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;">\\</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">validate</span><span style="--shiki-dark:#F286C4;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;">\\</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">ArticleValidate</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">class</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="工作原理-1" tabindex="-1">工作原理 <a class="header-anchor" href="#工作原理-1" aria-label="Permalink to "工作原理""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>add() 流程:</span></span>
|
||
<span class="line"><span> beforeAdd($data) → validateData($data, 'add') → 后续入库</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>update() 流程:</span></span>
|
||
<span class="line"><span> beforeUpdate($id, $data) → validateData($data, 'update', $id) → 后续入库</span></span></code></pre></div><h3 id="验证器示例" tabindex="-1">验证器示例 <a class="header-anchor" href="#验证器示例" aria-label="Permalink to "验证器示例""></a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleValidate</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> Validate</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">rule</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">title</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">require|max:200</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> ];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">message</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">title.require</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">文章标题不能为空</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> ];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">scene</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">add</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">title</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">],</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">update</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">title</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">],</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> ];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><p>校验失败抛出 <code>ValidateException</code>,由 <code>ExceptionHandle</code> 统一返回 <code>{"code":1, "msg":"参数验证失败", "data":"文章标题不能为空"}</code>。</p><h3 id="覆盖范围" tabindex="-1">覆盖范围 <a class="header-anchor" href="#覆盖范围" aria-label="Permalink to "覆盖范围""></a></h3><p>无论是 Controller 通过 <code>_add</code>/<code>_edit</code> 调用,还是 <code>import</code> 等内部调用都会触发校验。</p><h2 id="_7-唯一性校验" tabindex="-1">7. 唯一性校验 <a class="header-anchor" href="#_7-唯一性校验" aria-label="Permalink to "7. 唯一性校验""></a></h2><p><strong>配置方式</strong>:Logic 中设置 <code>uniqueFields</code></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> UserLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 全局唯一</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">uniqueFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">username</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">mobile</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="两种规则" tabindex="-1">两种规则 <a class="header-anchor" href="#两种规则" aria-label="Permalink to "两种规则""></a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 字符串 — 全局唯一</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">uniqueFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">username</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">mobile</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 数组 — 分组内唯一</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">uniqueFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">username</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // username 全局唯一</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">code</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">pid</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">],</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // code 在同一 pid 下唯一</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span></code></pre></div><h3 id="工作原理-2" tabindex="-1">工作原理 <a class="header-anchor" href="#工作原理-2" aria-label="Permalink to "工作原理""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>add() 时:</span></span>
|
||
<span class="line"><span> checkUnique($data)</span></span>
|
||
<span class="line"><span> → SELECT * FROM think_user WHERE username = 'admin'</span></span>
|
||
<span class="line"><span> → 存在 → throw new \\Exception('用户名已存在')</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>update() 时:</span></span>
|
||
<span class="line"><span> checkUnique($data, $excludeId)</span></span>
|
||
<span class="line"><span> → SELECT * FROM think_user WHERE username = 'admin' AND id <> 42</span></span>
|
||
<span class="line"><span> → 存在 → throw new \\Exception('用户名已存在')</span></span></code></pre></div><h3 id="错误提示自定义" tabindex="-1">错误提示自定义 <a class="header-anchor" href="#错误提示自定义" aria-label="Permalink to "错误提示自定义""></a></h3><p>默认提示格式:"字段中文名 + 已存在"。可通过重写 <code>getFieldLabel()</code> 自定义:</p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> getFieldLabel</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">field</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">:</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">map</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">username</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">用户名</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">mobile</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">手机号</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">code</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">编码</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> ];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> return</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">map</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">[$</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">field</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> ??</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">field</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h2 id="_8-数据权限过滤" tabindex="-1">8. 数据权限过滤 <a class="header-anchor" href="#_8-数据权限过滤" aria-label="Permalink to "8. 数据权限过滤""></a></h2><p><strong>配置方式</strong>:Logic 中设置 <code>dataScopeUserField</code> 和/或 <code>dataScopeDeptField</code></p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 数据权限:归属字段名(用于按创建人过滤)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">dataScopeUserField</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">create_user</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * 数据权限:部门关联字段名(用于按部门过滤)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> *</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@var</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> */</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">dataScopeDeptField</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">dept_id</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="三种权限范围" tabindex="-1">三种权限范围 <a class="header-anchor" href="#三种权限范围" aria-label="Permalink to "三种权限范围""></a></h3><p>由角色表 <code>think_role</code> 的 <code>data_scope</code> 字段决定:</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ data_scope = 1 → 全部数据(不过滤) │</span></span>
|
||
<span class="line"><span>│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │</span></span>
|
||
<span class="line"><span>│ │ A │ │ B │ │ C │ │ D │ │ E │ ← 所有部门所有人的数据 │</span></span>
|
||
<span class="line"><span>│ └───┘ └───┘ └───┘ └───┘ └───┘ │</span></span>
|
||
<span class="line"><span>├─────────────────────────────────────────────────────────┤</span></span>
|
||
<span class="line"><span>│ data_scope = 2 → 本部门数据 │</span></span>
|
||
<span class="line"><span>│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │</span></span>
|
||
<span class="line"><span>│ │ A │ │ B │ │███│ │ D │ │ E │ ← 只看到本部门数据 │</span></span>
|
||
<span class="line"><span>│ └───┘ └───┘ └───┘ └───┘ └───┘ │</span></span>
|
||
<span class="line"><span>├─────────────────────────────────────────────────────────┤</span></span>
|
||
<span class="line"><span>│ data_scope = 3 → 仅本人数据 │</span></span>
|
||
<span class="line"><span>│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │</span></span>
|
||
<span class="line"><span>│ │ A │ │ B │ │███│ │ D │ │ E │ ← 只看到自己创建的数据 │</span></span>
|
||
<span class="line"><span>│ └───┘ └───┘ └───┘ └───┘ └───┘ │</span></span>
|
||
<span class="line"><span>└─────────────────────────────────────────────────────────┘</span></span></code></pre></div><h3 id="工作原理-3" tabindex="-1">工作原理 <a class="header-anchor" href="#工作原理-3" aria-label="Permalink to "工作原理""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>查询时 applyDataScope():</span></span>
|
||
<span class="line"><span> 1. 获取当前用户角色 ID</span></span>
|
||
<span class="line"><span> 2. 查询角色的 data_scope</span></span>
|
||
<span class="line"><span> 3. scope=1 → 不过滤</span></span>
|
||
<span class="line"><span> 4. scope=2 → WHERE dept_id = 当前用户部门ID</span></span>
|
||
<span class="line"><span> 5. scope=3 → WHERE create_user = 当前用户名</span></span></code></pre></div><div class="warning custom-block"><p class="custom-block-title">无角色用户</p><p>无角色时默认按"仅本人"(scope=3)处理。</p></div><h2 id="_9-租户隔离" tabindex="-1">9. 租户隔离 <a class="header-anchor" href="#_9-租户隔离" aria-label="Permalink to "9. 租户隔离""></a></h2><p><strong>配置方式</strong>:继承 <code>BaseTenantLogic</code>(自动设置 <code>tenantScopeField = 'tenant_id'</code>)</p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 继承 BaseTenantLogic 即启用</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // tenantScopeField 已由基类设为 'tenant_id',无需再声明</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="隔离行为" tabindex="-1">隔离行为 <a class="header-anchor" href="#隔离行为" aria-label="Permalink to "隔离行为""></a></h3><table tabindex="0"><thead><tr><th>场景</th><th>行为</th><th>实现方法</th></tr></thead><tbody><tr><td><strong>查询时</strong></td><td>自动叠加 <code>WHERE tenant_id = 当前租户ID</code></td><td><code>applyTenantScope()</code></td></tr><tr><td><strong>新增时</strong></td><td>自动填充 <code>tenant_id</code> 字段</td><td><code>processTenantId()</code></td></tr><tr><td><strong>超级管理员</strong>(tenantId=0)</td><td>查询不过滤,可跨租户查看</td><td>条件判断跳过</td></tr><tr><td><strong>超级管理员新增</strong></td><td>自动代入默认租户(tenant_id=1)</td><td>读取配置 <code>tenant.default_tenant_id</code></td></tr></tbody></table><h3 id="租户上下文来源" tabindex="-1">租户上下文来源 <a class="header-anchor" href="#租户上下文来源" aria-label="Permalink to "租户上下文来源""></a></h3><p><code>TenantMiddleware</code> 解析 JWT 中的租户信息,注入 <code>$request->tenantId</code>。</p><h3 id="适用范围" tabindex="-1">适用范围 <a class="header-anchor" href="#适用范围" aria-label="Permalink to "适用范围""></a></h3><ul><li>需要租户隔离的模块:继承 <code>BaseTenantLogic</code>(User、Article、Dept、Level 等)</li><li>系统共享数据:继承 <code>BaseLogic</code>(Menu、Role、Dict、Config 等)</li></ul><h2 id="_10-审计字段-软删除-BaseModel-" tabindex="-1">10. 审计字段 + 软删除(BaseModel) <a class="header-anchor" href="#_10-审计字段-软删除-BaseModel-" aria-label="Permalink to "10. 审计字段 + 软删除(BaseModel)""></a></h2><p><strong>配置方式</strong>:继承 <code>BaseModel</code> 即自动生效,无需额外配置。</p><h3 id="软删除" tabindex="-1">软删除 <a class="header-anchor" href="#软删除" aria-label="Permalink to "软删除""></a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>全局查询范围 soft_delete:</span></span>
|
||
<span class="line"><span> 所有查询自动叠加 WHERE is_delete = 0</span></span>
|
||
<span class="line"><span> → 已删除数据默认不可见</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>软删除操作:</span></span>
|
||
<span class="line"><span> $model->softDelete() → UPDATE SET is_delete = 1</span></span>
|
||
<span class="line"><span> BaseModel::batchSoftDelete($ids) → 批量软删除</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>查询已删除数据:</span></span>
|
||
<span class="line"><span> Model::withoutGlobalScope(['soft_delete'])->...</span></span></code></pre></div><h3 id="审计字段自动写入" tabindex="-1">审计字段自动写入 <a class="header-anchor" href="#审计字段自动写入" aria-label="Permalink to "审计字段自动写入""></a></h3><table tabindex="0"><thead><tr><th>事件</th><th>自动填充字段</th><th>数据来源</th></tr></thead><tbody><tr><td><code>onBeforeInsert</code>(创建前)</td><td><code>create_user</code></td><td><code>$request->userInfo->username</code></td></tr><tr><td><code>onBeforeInsert</code>(创建前)</td><td><code>create_time</code></td><td><code>date('Y-m-d H:i:s')</code></td></tr><tr><td><code>onBeforeUpdate</code>(更新前)</td><td><code>update_user</code></td><td><code>$request->userInfo->username</code></td></tr><tr><td><code>onBeforeUpdate</code>(更新前)</td><td><code>update_time</code></td><td><code>date('Y-m-d H:i:s')</code>(始终覆盖)</td></tr></tbody></table><div class="tip custom-block"><p class="custom-block-title">无用户场景</p><p>控制台任务、定时任务等无登录用户的场景下,跳过用户字段,时间字段仍写入。</p></div><h3 id="数据库公共字段" tabindex="-1">数据库公共字段 <a class="header-anchor" href="#数据库公共字段" aria-label="Permalink to "数据库公共字段""></a></h3><p>所有业务表<strong>必须</strong>包含以下字段:</p><div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">id </span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">INT</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> PRIMARY KEY</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> AUTO_INCREMENT COMMENT </span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">主键</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">is_delete </span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">TINYINT</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">(</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;">1</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">) </span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">DEFAULT</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 0</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> COMMENT </span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">软删除 0=正常 1=已删除</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">create_user </span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">VARCHAR</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">(</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;">50</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">) </span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">DEFAULT</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> ''</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> COMMENT </span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">创建人</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">create_time </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">DATETIME</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> COMMENT </span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">创建时间</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">update_user </span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">VARCHAR</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">(</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;">50</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">) </span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">DEFAULT</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> ''</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> COMMENT </span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">更新人</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">,</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">update_time </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">DATETIME</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> COMMENT </span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">更新时间</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span></span></code></pre></div><h2 id="横切机制的组合使用" tabindex="-1">横切机制的组合使用 <a class="header-anchor" href="#横切机制的组合使用" aria-label="Permalink to "横切机制的组合使用""></a></h2><p>一个模块可以同时启用多种横切机制,它们互不干扰:</p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> ArticleLogic</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> extends</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#59873A;--shiki-light-font-style:inherit;"> BaseTenantLogic</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">modelClass</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> Article</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">class</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 自动参数校验</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">validateClass</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> ArticleValidate</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">class</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 文件字段处理</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">cover</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fileSaveDir</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">article</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 多文件字段处理</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">multiFileFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">images</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 富文本占位符</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">contentFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">content</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 枚举显示名翻译</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">serializeMaps</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =></span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">article_status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 唯一性校验</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">uniqueFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">title</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 数据权限</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">dataScopeUserField</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">create_user</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 租户隔离(继承 BaseTenantLogic 自动启用)</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 审计字段 + 软删除(继承 BaseModel 自动生效)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><p>执行顺序(add 流程):</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>beforeAdd ← 钩子</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>validateData ← 自动参数校验</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>camel_to_snake ← 驼峰转下划线</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>processTenantId ← 租户ID填充</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>processFileFields ← 文件字段处理(单值)</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>processMultiFileFields ← 多文件字段处理(逗号分隔多值)</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>processContentFields ← 富文本处理</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>filterTableFields ← 过滤非数据库字段</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>checkUnique ← 唯一性校验</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>Model::create ← 审计字段自动填充</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>afterAdd ← 钩子</span></span></code></pre></div>`,102)])])}const g=i(p,[["render",l]]);export{o as __pageData,g as default};
|