230 lines
58 KiB
JavaScript
230 lines
58 KiB
JavaScript
import{_ as a,o as i,c as n,a2 as t}from"./chunks/framework.B-QBKwc1.js";const c=JSON.parse('{"title":"后端分层架构","description":"后端采用\\"三层主干 + 三层支撑\\"架构:Controller → Logic → Model 构成主干链路,Validate、Service、Middleware 分别提供参数校验、通用能力、","frontmatter":{},"headers":[],"relativePath":"zh/architecture/backend-layer.md","filePath":"zh/architecture/backend-layer.md"}'),l={name:"zh/architecture/backend-layer.md"};function e(p,s,h,d,k,r){return i(),n("div",null,[...s[0]||(s[0]=[t(`<h1 id="_4-3-后端分层架构" tabindex="-1">4.3 后端分层架构 <a class="header-anchor" href="#_4-3-后端分层架构" aria-label="Permalink to "4.3 后端分层架构""></a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>后端采用"三层主干 + 三层支撑"架构:Controller → Logic → Model 构成主干链路,Validate、Service、Middleware 分别提供参数校验、通用能力、请求拦截等支撑。各层职责明确,层间通过方法调用串联,不越层访问。</p></div><h2 id="架构全景" tabindex="-1">架构全景 <a class="header-anchor" href="#架构全景" aria-label="Permalink to "架构全景""></a></h2><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>│ 支撑层 │</span></span>
|
||
<span class="line"><span>│ │</span></span>
|
||
<span class="line"><span>│ Validate(参数校验) Service(通用能力) Middleware(请求拦截) │</span></span>
|
||
<span class="line"><span>│ app/validate/*.php app/service/*.php app/middleware/*.php │</span></span>
|
||
<span class="line"><span>└──────────────────────────────┬──────────────────────────────────────┘</span></span>
|
||
<span class="line"><span> │ 被主干层调用</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>┌─────────────────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 主干链路 │</span></span>
|
||
<span class="line"><span>│ │</span></span>
|
||
<span class="line"><span>│ ┌─────────────────────────────────────────────────────────────┐ │</span></span>
|
||
<span class="line"><span>│ │ Controller(控制器层) │ │</span></span>
|
||
<span class="line"><span>│ │ 接收请求 → 获取参数 → 调用 Logic → 封装响应 │ │</span></span>
|
||
<span class="line"><span>│ │ 基类:BaseController | 文件:app/controller/*.php │ │</span></span>
|
||
<span class="line"><span>│ └──────────────────────────┬──────────────────────────────────┘ │</span></span>
|
||
<span class="line"><span>│ │ 调用 Logic 方法 │</span></span>
|
||
<span class="line"><span>│ ▼ │</span></span>
|
||
<span class="line"><span>│ ┌─────────────────────────────────────────────────────────────┐ │</span></span>
|
||
<span class="line"><span>│ │ Logic(逻辑层) │ │</span></span>
|
||
<span class="line"><span>│ │ 属性配置 → 参数校验 → 生命周期钩子 → 横切处理 → 调用 Model │ │</span></span>
|
||
<span class="line"><span>│ │ 基类:BaseLogic / BaseTenantLogic | 文件:app/logic/*.php │ │</span></span>
|
||
<span class="line"><span>│ └──────────────────────────┬──────────────────────────────────┘ │</span></span>
|
||
<span class="line"><span>│ │ 调用 Model ORM │</span></span>
|
||
<span class="line"><span>│ ▼ │</span></span>
|
||
<span class="line"><span>│ ┌─────────────────────────────────────────────────────────────┐ │</span></span>
|
||
<span class="line"><span>│ │ Model(模型层) │ │</span></span>
|
||
<span class="line"><span>│ │ 表映射 → 软删除 → 审计字段 → ORM → SQL │ │</span></span>
|
||
<span class="line"><span>│ │ 基类:BaseModel | 文件:app/model/*.php │ │</span></span>
|
||
<span class="line"><span>│ └──────────────────────────┬──────────────────────────────────┘ │</span></span>
|
||
<span class="line"><span>│ │ │</span></span>
|
||
<span class="line"><span>│ ▼ │</span></span>
|
||
<span class="line"><span>│ ┌─────────────────────────────────────────────────────────────┐ │</span></span>
|
||
<span class="line"><span>│ │ Database(数据库) │ │</span></span>
|
||
<span class="line"><span>│ └─────────────────────────────────────────────────────────────┘ │</span></span>
|
||
<span class="line"><span>└─────────────────────────────────────────────────────────────────────┘</span></span></code></pre></div><h2 id="各层职责详解" tabindex="-1">各层职责详解 <a class="header-anchor" href="#各层职责详解" aria-label="Permalink to "各层职责详解""></a></h2><h3 id="Controller-层" tabindex="-1">Controller 层 <a class="header-anchor" href="#Controller-层" aria-label="Permalink to "Controller 层""></a></h3><p><strong>文件</strong>:<code>app/controller/{Module}Controller.php</code><strong>基类</strong>:<code>BaseController</code><strong>职责</strong>:HTTP 请求入口,只做"搬运"不做"判断"</p><table tabindex="0"><thead><tr><th>职责</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>参数获取</td><td>从请求中提取参数</td><td><code>$this->getJsonBody()</code>、<code>$this->getParams()</code></td></tr><tr><td>调用 Logic</td><td>委托业务逻辑</td><td><code>$this->logic->add($data)</code></td></tr><tr><td>响应封装</td><td>统一 JSON 格式返回</td><td><code>$this->success()</code>、<code>$this->fail()</code></td></tr><tr><td>注解标注</td><td>声明权限和日志</td><td><code>#[Permission]</code>、<code>#[Log]</code></td></tr></tbody></table><p><strong>Controller 不应该做:</strong> 业务判断、数据库操作、文件处理、参数校验(特殊场景除外)。</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;"> ArticleController</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;"> BaseController</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:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> ArticleLogic</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">logic</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"></span>
|
||
<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;"> initialize</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;"> void</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;"> $</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#A65E2B;--shiki-light-font-style:inherit;">this</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">-></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">logic</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> new</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> ArticleLogic</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>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 标准 CRUD — 一行调用</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> #[</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">Permission</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;">sys:article:page</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;">文章分页</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">]</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> public</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> page</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">()</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> Json</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:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#AB5959;--shiki-light-font-style:inherit;"> parent</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">_index</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;">$</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#A65E2B;--shiki-light-font-style:inherit;">this</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">-></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">logic</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>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;"> #[</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">Permission</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;">sys:article:add</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;">添加文章</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">]</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> public</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> add</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">()</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> Json</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:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#AB5959;--shiki-light-font-style:inherit;"> parent</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">_add</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;">$</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#A65E2B;--shiki-light-font-style:inherit;">this</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">-></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">logic</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;"> $</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#A65E2B;--shiki-light-font-style:inherit;">this</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">-></span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">getJsonBody</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="Logic-层" tabindex="-1">Logic 层 <a class="header-anchor" href="#Logic-层" aria-label="Permalink to "Logic 层""></a></h3><p><strong>文件</strong>:<code>app/logic/{Module}Logic.php</code><strong>基类</strong>:<code>BaseLogic</code>(系统共享)/ <code>BaseTenantLogic</code>(租户隔离) <strong>职责</strong>:业务核心,通过"属性配置 + 生命周期钩子"声明式 CRUD</p><table tabindex="0"><thead><tr><th>职责</th><th>方式</th><th>说明</th></tr></thead><tbody><tr><td>行为定义</td><td>属性配置</td><td><code>$pageLikeFields</code>、<code>$uniqueFields</code>、<code>$validateClass</code> 等</td></tr><tr><td>业务定制</td><td>生命周期钩子</td><td><code>beforeAdd</code>、<code>afterUpdate</code>、<code>beforeDelete</code> 等</td></tr><tr><td>自动校验</td><td><code>$validateClass</code></td><td>add/update 时自动调用验证器</td></tr><tr><td>横切处理</td><td>属性驱动</td><td>文件字段、富文本、枚举映射、数据权限、租户隔离</td></tr></tbody></table><p><strong>Logic 不应该做:</strong> 直接 HTTP 交互、直接返回 JSON 响应。</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;">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:#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;">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:#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;">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>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> /**</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * LIKE 模糊匹配字段</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;">pageLikeFields</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:#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;">pageEqFields</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:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">category_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>
|
||
<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;">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:#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 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:#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;">@param</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> $data 待新增的数据</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> * </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">@return</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><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:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> beforeAdd</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</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;">data</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;"> array</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;">data</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;">sort</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">data</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;">sort</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> ??</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 0</span><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;">data</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="Model-层" tabindex="-1">Model 层 <a class="header-anchor" href="#Model-层" aria-label="Permalink to "Model 层""></a></h3><p><strong>文件</strong>:<code>app/model/{TableName}.php</code><strong>基类</strong>:<code>BaseModel</code><strong>职责</strong>:数据持久化,只做表映射和自动维护</p><table tabindex="0"><thead><tr><th>职责</th><th>说明</th></tr></thead><tbody><tr><td>表映射</td><td><code>protected $name = 'article'</code></td></tr><tr><td>软删除</td><td>全局查询范围自动过滤 <code>is_delete=1</code></td></tr><tr><td>审计字段</td><td><code>onBeforeInsert</code>/<code>onBeforeUpdate</code> 自动填充</td></tr><tr><td>关联关系</td><td><code>hasMany</code>、<code>belongsTo</code> 等</td></tr></tbody></table><p><strong>Model 不应该做:</strong> 业务判断、复杂查询逻辑(交给 Logic)。</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;"> Article</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;"> BaseModel</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;">name</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;"> public</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> category</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:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;"> $</span><span style="--shiki-dark:#BF9EEE;--shiki-dark-font-style:italic;--shiki-light:#A65E2B;--shiki-light-font-style:inherit;">this</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">-></span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">belongsTo</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">Category</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 style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> '</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">category_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>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="Validate-层" tabindex="-1">Validate 层 <a class="header-anchor" href="#Validate-层" aria-label="Permalink to "Validate 层""></a></h3><p><strong>文件</strong>:<code>app/validate/{Module}Validate.php</code><strong>基类</strong>:<code>think\\Validate</code><strong>职责</strong>:参数格式校验,定义规则 + 场景</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;"> 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><strong>触发方式:</strong> Logic 配置 <code>$validateClass</code> 后,add/update 自动调用。</p><h3 id="Service-层" tabindex="-1">Service 层 <a class="header-anchor" href="#Service-层" aria-label="Permalink to "Service 层""></a></h3><p><strong>文件</strong>:<code>app/service/{Name}Service.php</code><strong>职责</strong>:跨模块通用能力,无状态、无副作用</p><table tabindex="0"><thead><tr><th>服务</th><th>能力</th></tr></thead><tbody><tr><td><code>DictService</code></td><td>字典查询(两级缓存)</td></tr><tr><td><code>PasswordService</code></td><td>密码加密(bcrypt 双重加盐)</td></tr><tr><td><code>JwtService</code></td><td>Token 生成/刷新/解析</td></tr><tr><td><code>ExcelService</code></td><td>Excel 导入导出</td></tr><tr><td><code>CaptchaService</code></td><td>图片验证码</td></tr></tbody></table><p><strong>Service 不应该做:</strong> 持有状态、直接操作数据库(通过 Model 静态方法例外)。</p><h3 id="Middleware-层" tabindex="-1">Middleware 层 <a class="header-anchor" href="#Middleware-层" aria-label="Permalink to "Middleware 层""></a></h3><p><strong>文件</strong>:<code>app/middleware/{Name}Middleware.php</code><strong>职责</strong>:请求拦截,在 Controller 前后执行</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>CorsMiddleware → AuthMiddleware → TenantMiddleware → DemoMiddleware → LogMiddleware → Controller</span></span></code></pre></div><table tabindex="0"><thead><tr><th>中间件</th><th>作用</th><th>注入数据</th></tr></thead><tbody><tr><td><code>AuthMiddleware</code></td><td>JWT 认证 + 权限校验</td><td><code>$request->userInfo</code></td></tr><tr><td><code>TenantMiddleware</code></td><td>租户上下文</td><td><code>$request->tenantId</code></td></tr><tr><td><code>DemoMiddleware</code></td><td>演示环境拦截</td><td>—</td></tr><tr><td><code>LogMiddleware</code></td><td>操作日志记录</td><td>—</td></tr></tbody></table><h2 id="各层方法对照表" tabindex="-1">各层方法对照表 <a class="header-anchor" href="#各层方法对照表" aria-label="Permalink to "各层方法对照表""></a></h2><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><th>输出</th></tr></thead><tbody><tr><td>Controller</td><td><code>_index($logic)</code></td><td>HTTP 请求参数</td><td>Json 响应</td></tr><tr><td>Logic</td><td><code>pageList($params, $where, $with)</code></td><td>参数数组</td><td><code>['records'=>[], 'total'=>int, ...]</code></td></tr><tr><td>Model</td><td><code>Model::page($current, $size)->select()</code></td><td>分页条件</td><td>Collection</td></tr></tbody></table><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><th>输出</th></tr></thead><tbody><tr><td>Controller</td><td><code>_add($logic, $data)</code> 或 <code>$this->logic->add($data)</code></td><td>Json 请求体</td><td>Json 响应</td></tr><tr><td>Logic</td><td><code>add($data)</code></td><td>关联数组</td><td><code>int</code>(新记录 ID)</td></tr><tr><td>Model</td><td><code>Model::create($data)</code></td><td>关联数组</td><td>Model 实例</td></tr></tbody></table><p><strong>Logic::add 内部流程:</strong><code>beforeAdd</code> → <code>validateData</code> → <code>camel_to_snake</code> → <code>processTenantId</code> → <code>processFile</code> → <code>processContent</code> → <code>filterTableFields</code> → <code>checkUnique</code> → <code>Model::create</code> → <code>afterAdd</code></p><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><th>输出</th></tr></thead><tbody><tr><td>Controller</td><td><code>_edit($logic, $id, $data)</code></td><td>id + Json 请求体</td><td>Json 响应</td></tr><tr><td>Logic</td><td><code>update($id, $data)</code></td><td>id + 关联数组</td><td><code>bool</code></td></tr><tr><td>Model</td><td><code>Model::find($id)->save($data)</code></td><td>模型实例 + 关联数组</td><td><code>bool</code></td></tr></tbody></table><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><th>输出</th></tr></thead><tbody><tr><td>Controller</td><td><code>_remove($logic, $id)</code></td><td>id</td><td>Json 响应</td></tr><tr><td>Logic</td><td><code>delete($id)</code></td><td>id</td><td><code>bool</code></td></tr><tr><td>Model</td><td><code>$model->save(['is_delete' => 1])</code></td><td>软删除标记</td><td><code>bool</code></td></tr></tbody></table><h2 id="数据流向" tabindex="-1">数据流向 <a class="header-anchor" href="#数据流向" aria-label="Permalink to "数据流向""></a></h2><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>前端请求(camelCase JSON)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseController::getJsonBody() ← 获取原始参数</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseLogic::beforeAdd($data) ← 钩子:预处理</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseLogic::validateData($data) ← 自动参数校验</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseLogic::convertQueryParams() ← camelCase → snake_case</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseLogic::processFileFieldsOnSave ← 文件字段处理</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseLogic::filterTableFields() ← 过滤非数据库字段</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseLogic::checkUnique() ← 唯一性校验</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseModel::create() ← 审计字段自动填充</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>Database(snake_case)</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>Database(snake_case 数据)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseModel ← ORM 查询</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>BaseLogic 处理</span></span>
|
||
<span class="line"><span> │ processFileFieldsOnRead() ← 文件字段补全域名</span></span>
|
||
<span class="line"><span> │ processContentFieldsOnRead() ← 富文本占位符替换</span></span>
|
||
<span class="line"><span> │ processSerializeMaps() ← 枚举显示名补全</span></span>
|
||
<span class="line"><span> │ afterDetail() ← 钩子:补充关联数据</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>Result::convertData() ← snake_case → camelCase</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>JSON 响应 → 前端</span></span></code></pre></div><h2 id="异常处理传播" tabindex="-1">异常处理传播 <a class="header-anchor" href="#异常处理传播" aria-label="Permalink to "异常处理传播""></a></h2><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>Model 层异常(如数据库连接失败)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 向上抛出</span></span>
|
||
<span class="line"><span>Logic 层异常(如 beforeDelete 抛出业务异常、validateData 抛出 ValidateException)</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼ 向上抛出</span></span>
|
||
<span class="line"><span>Controller 层</span></span>
|
||
<span class="line"><span> │ 方式一:try-catch 捕获,返回 fail()</span></span>
|
||
<span class="line"><span> │ 方式二:不捕获,由 ExceptionHandle 统一处理</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>ExceptionHandle::render()</span></span>
|
||
<span class="line"><span> │ ValidateException → {"code":1, "msg":"参数验证失败", "data":"..."}</span></span>
|
||
<span class="line"><span> │ ModelNotFoundException → {"code":1, "msg":"数据不存在"}</span></span>
|
||
<span class="line"><span> │ RuntimeException(401) → {"code":1, "msg":"未授权"}</span></span>
|
||
<span class="line"><span> │ 其他异常 → {"code":1, "msg":"操作失败"}</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>JSON 响应</span></span></code></pre></div><p><strong>推荐做法:</strong></p><ul><li>Controller 通过 <code>_add</code>/<code>_edit</code>/<code>_remove</code> 调用时,不捕获异常,交给 ExceptionHandle</li><li>Controller 直接调用 <code>$this->logic->add()</code> 时,try-catch 捕获并返回 <code>$this->fail()</code></li><li>Logic 层的业务异常(如 beforeDelete 校验)直接 <code>throw new \\Exception</code></li></ul><h2 id="完整请求示例" tabindex="-1">完整请求示例 <a class="header-anchor" href="#完整请求示例" aria-label="Permalink to "完整请求示例""></a></h2><p>以"新增文章"为例,展示一个请求从进入到返回的完整链路:</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>POST /api/article/add</span></span>
|
||
<span class="line"><span>Content-Type: application/json</span></span>
|
||
<span class="line"><span>Authorization: Bearer <token></span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>{"title": "新文章", "categoryId": 1, "status": 0}</span></span></code></pre></div><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>1. CorsMiddleware → 放行(设置跨域头)</span></span>
|
||
<span class="line"><span>2. AuthMiddleware → 校验 Token → 注入 $request->userInfo</span></span>
|
||
<span class="line"><span>3. TenantMiddleware → 解析租户 → 注入 $request->tenantId</span></span>
|
||
<span class="line"><span>4. LogMiddleware → 记录日志(#[Log] 注解)</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>5. ArticleController::add()</span></span>
|
||
<span class="line"><span> │ getJsonBody() → ["title"=>"新文章", "categoryId"=>1, "status"=>0]</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>6. ArticleLogic::add($data)</span></span>
|
||
<span class="line"><span> │ beforeAdd($data) → 设置默认 sort=0</span></span>
|
||
<span class="line"><span> │ validateData($data, 'add') → 校验 title 必填 ✓</span></span>
|
||
<span class="line"><span> │ camel_to_snake($data) → ["title"=>"新文章", "category_id"=>1, "status"=>0]</span></span>
|
||
<span class="line"><span> │ processTenantId($data) → 加入 tenant_id</span></span>
|
||
<span class="line"><span> │ processFileFieldsOnSave → cover 无值,跳过</span></span>
|
||
<span class="line"><span> │ filterTableFields($data) → 过滤非表字段</span></span>
|
||
<span class="line"><span> │ checkUnique($data) → title 唯一 ✓</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>7. Article::create($data)</span></span>
|
||
<span class="line"><span> │ onBeforeInsert → 自动填充 create_user, create_time</span></span>
|
||
<span class="line"><span> │</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>8. Database INSERT</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>9. ArticleLogic::afterAdd($id, $data) → 无额外操作</span></span>
|
||
<span class="line"><span></span></span>
|
||
<span class="line"><span>10. 返回 {"code":0, "ok":true, "msg":"添加成功", "data":{"id":42}}</span></span></code></pre></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></tr></thead><tbody><tr><td>Controller → Logic</td><td>✅ 允许,标准调用</td></tr><tr><td>Controller → Model</td><td>❌ 禁止,越层</td></tr><tr><td>Logic → Model</td><td>✅ 允许,标准调用</td></tr><tr><td>Logic → Service</td><td>✅ 允许,调用通用能力</td></tr><tr><td>Logic → Controller</td><td>❌ 禁止,反向依赖</td></tr><tr><td>Model → Logic</td><td>❌ 禁止,反向依赖</td></tr><tr><td>Model → Service</td><td>⚠️ 慎用,仅静态工具方法</td></tr><tr><td>Middleware → Controller</td><td>✅ 通过 <code>$request</code> 注入数据</td></tr><tr><td>Validate → Logic</td><td>❌ 禁止,只做格式校验</td></tr></tbody></table>`,57)])])}const g=a(l,[["render",e]]);export{c as __pageData,g as default};
|