Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_EleVue/.vitepress/dist/assets/zh_architecture_backend-layer.md.BZLK6j4N.lean.js
2026-10-25 18:52:36 +08:00

181 lines
53 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 → Service → Model 构成主干链路,FormRequest、横切服务、Middleware 分别提供参数校验、通用能力、","frontmatter":{},"headers":[],"relativePath":"zh/architecture/backend-layer.md","filePath":"zh/architecture/backend-layer.md"}'),e={name:"zh/architecture/backend-layer.md"};function l(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 &quot;4.3 后端分层架构&quot;">​</a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>后端采用&quot;三层主干 + 三层支撑&quot;架构:Controller → Service → Model 构成主干链路,FormRequest、横切服务、Middleware 分别提供参数校验、通用能力、请求拦截等支撑。各层职责明确,层间通过方法调用串联,不越层访问。</p></div><h2 id="架构全景" tabindex="-1">架构全景 <a class="header-anchor" href="#架构全景" aria-label="Permalink to &quot;架构全景&quot;">​</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>│ FormRequest(参数校验) 横切 Service(通用能力) Middleware(请求拦截)│</span></span>
<span class="line"><span>│ Http/Requests/*.php Services/(Jwt/Dict…) Http/Middleware/ │</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>│ │ 注解标注 → 参数注入 → 调用 Service → 封装响应 │ │</span></span>
<span class="line"><span>│ │ 基类:Controller | 文件:app/Http/Controllers/Api/*.php │ │</span></span>
<span class="line"><span>│ └──────────────────────────┬──────────────────────────────────┘ │</span></span>
<span class="line"><span>│ │ 调用 Service 方法 │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ ┌─────────────────────────────────────────────────────────────┐ │</span></span>
<span class="line"><span>│ │ Service(服务层) │ │</span></span>
<span class="line"><span>│ │ 属性配置 → 唯一性校验 → 生命周期钩子 → 事务 → 调用 Model │ │</span></span>
<span class="line"><span>│ │ 基类:BaseService / BaseTenantService | Services/*.php │ │</span></span>
<span class="line"><span>│ └──────────────────────────┬──────────────────────────────────┘ │</span></span>
<span class="line"><span>│ │ 调用 Eloquent 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>│ │ Trait:SoftDeletesByFlag / HasAuditColumns | Models/*.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>│ │ Database(五种数据库:MySQL / PG / SQL Server / Oracle / SQLite)│</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 &quot;各层职责详解&quot;">​</a></h2><h3 id="Controller-层" tabindex="-1">Controller 层 <a class="header-anchor" href="#Controller-层" aria-label="Permalink to &quot;Controller 层&quot;">​</a></h3><p><strong>文件</strong>:<code>app/Http/Controllers/Api/{Module}Controller.php</code><strong>基类</strong>:<code>App\\Http\\Controllers\\Controller</code><strong>职责</strong>:HTTP 请求入口,只做&quot;编排&quot;不做&quot;判断&quot;</p><table tabindex="0"><thead><tr><th>职责</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td>参数注入</td><td>类型注入 FormRequest 完成格式校验</td><td><code>add(ExampleRequest $request)</code></td></tr><tr><td>调用 Service</td><td>委托业务逻辑(快捷方法一行调用)</td><td><code>$this-&gt;_add($service, $data)</code></td></tr><tr><td>响应封装</td><td>统一 JSON 格式返回</td><td><code>$this-&gt;success()</code> / <code>$this-&gt;fail()</code></td></tr><tr><td>注解标注</td><td>声明权限和日志</td><td><code>#[Permission]</code>、<code>#[OperationLog]</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;"> ExampleController</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;"> Controller</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;"> public</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> function</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;"> __construct</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><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;"> ExampleService</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">exampleService</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</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;"> // 标准 CRUD —— 一行调用(构造注入,无需 initialize)</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;">OperationLog</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">示例管理-查询分页</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;"> OperationLog</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#A65E2B;">TYPE_QUERY</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:#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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">sys:example:page</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">示例分页</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;"> JsonResponse</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;">-&gt;</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">_page</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;">-&gt;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">exampleService</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;">OperationLog</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">示例管理-新增</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;"> OperationLog</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#A65E2B;">TYPE_ADD</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">新增示例:{name}</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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:#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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">sys:example:add</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">添加示例</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">ExampleRequest</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">request</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;"> JsonResponse</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;">-&gt;</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;">-&gt;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">exampleService</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">request</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">-&gt;</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">validated</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="Service-层" tabindex="-1">Service 层 <a class="header-anchor" href="#Service-层" aria-label="Permalink to &quot;Service 层&quot;">​</a></h3><p><strong>文件</strong>:<code>app/Services/{Module}Service.php</code><strong>基类</strong>:<code>BaseService</code>(系统共享)/ <code>BaseTenantService</code>(租户隔离) <strong>职责</strong>:业务核心,通过&quot;属性配置 + 生命周期钩子&quot;声明式 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>$serializeMaps</code> 等 16 个声明式属性</td></tr><tr><td>业务定制</td><td>生命周期钩子</td><td><code>beforeAdd</code>、<code>afterUpdate</code>、<code>beforeDelete</code> 等 10 个钩子</td></tr><tr><td>横切处理</td><td>Trait 组合</td><td>文件字段、字典翻译、数据权限、租户隔离(6 个 Concerns Trait)</td></tr><tr><td>数据安全</td><td><code>DB::transaction</code></td><td>add/update/delete/batchDelete 主流程事务包裹</td></tr></tbody></table><p><strong>Service 不应该做</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;"> ExampleService</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;"> BaseService</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;"> use</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> HasCrudOperations</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;"> HasQueryScopes</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;"> HasSerializeMaps</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;"> HasUniqueCheck</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:#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;"> Example</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:#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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">name</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;"> 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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;"> 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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">name</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;"> 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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =&gt;</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">example_status</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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:#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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">sort</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">sort</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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 &quot;Model 层&quot;">​</a></h3><p><strong>文件</strong>:<code>app/Models/{TableName}.php</code><strong>基类</strong>:Eloquent Model + Concerns Trait <strong>职责</strong>:数据持久化,只做表映射和自动维护</p><table tabindex="0"><thead><tr><th>职责</th><th>说明</th></tr></thead><tbody><tr><td>表映射</td><td><code>protected $table = &#39;example&#39;</code>(不含前缀,<code>DB_PREFIX</code> 自动拼接)</td></tr><tr><td>软删除</td><td><code>SoftDeletesByFlag</code> 注册全局范围自动过滤 <code>is_delete=1</code></td></tr><tr><td>审计字段</td><td><code>HasAuditColumns</code> 的 <code>creating/updating</code> 事件自动填充</td></tr><tr><td>关联关系</td><td><code>hasMany</code>、<code>belongsTo</code>、<code>belongsToMany</code> 等</td></tr></tbody></table><p><strong>Model 不应该做</strong>:业务判断、复杂查询编排(交给 Service)。</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;"> Example</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;"> Model</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;"> use</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> HasAuditColumns</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;"> SoftDeletesByFlag</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:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">table</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">example</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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;">-&gt;</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;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">category_id</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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="FormRequest-层-参数校验支撑-" tabindex="-1">FormRequest 层(参数校验支撑) <a class="header-anchor" href="#FormRequest-层-参数校验支撑-" aria-label="Permalink to &quot;FormRequest 层(参数校验支撑)&quot;">​</a></h3><p><strong>文件</strong>:<code>app/Http/Requests/{Module}Request.php</code><strong>基类</strong>:<code>BaseRequest</code><strong>职责</strong>:参数<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;"> ExampleRequest</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;"> BaseRequest</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;"> public</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> function</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> rules</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:#F286C4;--shiki-light:#1E754F;"> return</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">name</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =&gt;</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">required|string|max:255</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">type</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> =&gt;</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">nullable|integer|in:1,2,3,4</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><p><strong>触发方式</strong>:控制器方法类型注入,方法执行前自动校验;失败由 <code>failedValidation()</code> 返回统一 JSON(<code>code=1</code> + 字段明细)。</p><h3 id="横切-Service-通用能力支撑-" tabindex="-1">横切 Service(通用能力支撑) <a class="header-anchor" href="#横切-Service-通用能力支撑-" aria-label="Permalink to &quot;横切 Service(通用能力支撑)&quot;">​</a></h3><p><strong>文件</strong>:<code>app/Services/{Name}Service.php</code>(横切类) <strong>职责</strong>:跨模块通用能力,无状态、无副作用</p><table tabindex="0"><thead><tr><th>服务</th><th>能力</th></tr></thead><tbody><tr><td><code>JwtService</code></td><td>令牌签发/刷新/解析(Redis 有状态)</td></tr><tr><td><code>DictCacheService</code></td><td>字典查询(两级缓存)</td></tr><tr><td><code>PasswordService</code></td><td>密码加密(bcrypt,成本因子 12)</td></tr><tr><td><code>ExcelService</code></td><td>Excel 导入导出(表头映射、分批)</td></tr><tr><td><code>CaptchaService</code></td><td>图片验证码(IP 限流)</td></tr><tr><td><code>RequestInfoService</code></td><td>IP 归属地 / OS / 浏览器解析</td></tr></tbody></table><p><strong>横切 Service 不应该做</strong>:持有请求状态、直接返回响应。</p><h3 id="Middleware-层-请求拦截支撑-" tabindex="-1">Middleware 层(请求拦截支撑) <a class="header-anchor" href="#Middleware-层-请求拦截支撑-" aria-label="Permalink to &quot;Middleware 层(请求拦截支撑)&quot;">​</a></h3><p><strong>文件</strong>:<code>app/Http/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>ProxyMiddleware → Cors → ForceJsonResponse → Authenticate → ResolveTenant → LogOperation → Controller</span></span></code></pre></div><table tabindex="0"><thead><tr><th>中间件</th><th>作用</th><th>注入数据</th></tr></thead><tbody><tr><td><code>Authenticate</code></td><td>JWT 认证 + 权限校验</td><td><code>attributes[&#39;userInfo&#39;]</code></td></tr><tr><td><code>ResolveTenant</code></td><td>租户上下文</td><td><code>attributes[&#39;tenantId&#39;]</code> / <code>isSuperAdmin</code> / <code>tenantInfo</code></td></tr><tr><td><code>LogOperation</code></td><td>操作日志(handle 采集,terminate 落库)</td><td>—</td></tr></tbody></table><p>详见 <a href="/zh/architecture/request-flow.html">4.2 请求处理链路</a>。</p><h2 id="各层方法对照表" tabindex="-1">各层方法对照表 <a class="header-anchor" href="#各层方法对照表" aria-label="Permalink to &quot;各层方法对照表&quot;">​</a></h2><h3 id="分页查询链路" tabindex="-1">分页查询链路 <a class="header-anchor" href="#分页查询链路" aria-label="Permalink to &quot;分页查询链路&quot;">​</a></h3><table tabindex="0"><thead><tr><th>层</th><th>方法</th><th>输入</th><th>输出</th></tr></thead><tbody><tr><td>Controller</td><td><code>_page($service)</code></td><td>HTTP 请求参数</td><td>JsonResponse</td></tr><tr><td>Service</td><td><code>pageList($params, $where, $with)</code></td><td>参数数组</td><td><code>[&#39;records&#39;=&gt;[], &#39;total&#39;=&gt;int, &#39;current&#39;=&gt;int, &#39;size&#39;=&gt;int]</code></td></tr><tr><td>Model</td><td><code>Model::query()-&gt;paginate(...)</code></td><td>分页条件</td><td>LengthAwarePaginator</td></tr></tbody></table><h3 id="新增链路" tabindex="-1">新增链路 <a class="header-anchor" href="#新增链路" aria-label="Permalink to &quot;新增链路&quot;">​</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($service, $data)</code></td><td>FormRequest 校验后的数组</td><td>JsonResponse</td></tr><tr><td>Service</td><td><code>add($data)</code></td><td>snake_case 关联数组</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>Service::add 内部流程</strong>(HasCrudOperations):</p><p><code>beforeAdd</code> → <code>checkUnique</code> → <code>DB::transaction { Model::create(HasAuditColumns 审计填充)}</code> → <code>afterAdd</code></p><h3 id="修改链路" tabindex="-1">修改链路 <a class="header-anchor" href="#修改链路" aria-label="Permalink to &quot;修改链路&quot;">​</a></h3><table tabindex="0"><thead><tr><th>层</th><th>方法</th><th>输入</th><th>输出</th></tr></thead><tbody><tr><td>Controller</td><td><code>_update($service, $id, $data)</code></td><td>id + 请求数据</td><td>JsonResponse</td></tr><tr><td>Service</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)-&gt;update($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 &quot;删除链路&quot;">​</a></h3><table tabindex="0"><thead><tr><th>层</th><th>方法</th><th>输入</th><th>输出</th></tr></thead><tbody><tr><td>Controller</td><td><code>_delete($service, $id)</code></td><td>id</td><td>JsonResponse</td></tr><tr><td>Service</td><td><code>delete($id)</code></td><td>id</td><td><code>bool</code></td></tr><tr><td>Model</td><td><code>$model-&gt;softDelete()</code></td><td>软删除标记</td><td><code>bool</code>(<code>UPDATE SET is_delete=1</code>)</td></tr></tbody></table><h2 id="数据流向" tabindex="-1">数据流向 <a class="header-anchor" href="#数据流向" aria-label="Permalink to &quot;数据流向&quot;">​</a></h2><h3 id="请求方向-前端-→-后端-→-数据库-" tabindex="-1">请求方向(前端 → 后端 → 数据库) <a class="header-anchor" href="#请求方向-前端-→-后端-→-数据库-" aria-label="Permalink to &quot;请求方向(前端 → 后端 → 数据库)&quot;">​</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>FormRequest::validated() ← 格式校验(camelCase 规则),原样传递</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Controller::_add($service, $data) ← 编排 + 注解元数据</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Service::doAdd 入口 ← 键名统一转为 snake_case</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Service::beforeAdd($data) ← 钩子:预处理</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Service::checkUnique($data) ← 唯一性校验(BizException 拦截)</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>DB::transaction {</span></span>
<span class="line"><span> Service::processTenantId($data) ← 租户填充(HasDataScope 启用时)</span></span>
<span class="line"><span> Service::processFileFieldsOnSave ← 文件字段处理(HasFileProcessing 启用时)</span></span>
<span class="line"><span> Model::create($data) ← 审计字段自动填充(creating 事件)</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(snake_case)</span></span></code></pre></div><h3 id="响应方向-数据库-→-后端-→-前端-" tabindex="-1">响应方向(数据库 → 后端 → 前端) <a class="header-anchor" href="#响应方向-数据库-→-后端-→-前端-" aria-label="Permalink to &quot;响应方向(数据库 → 后端 → 前端)&quot;">​</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>Model ← ORM 查询(软删除全局范围生效)</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Service 处理(processAfterRead 处理链)</span></span>
<span class="line"><span> │ processFileFieldsOnRead() ← 文件字段补全域名(启用时)</span></span>
<span class="line"><span> │ processSerializeMapsList() ← 字典显示名补全(status → statusText)</span></span>
<span class="line"><span> │ afterDetail()/afterPageList() ← 钩子:补充关联数据</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Result::success() / page() ← 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 &quot;异常处理传播&quot;">​</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>Service 层异常</span></span>
<span class="line"><span> │ beforeAdd/beforeDelete 抛出 BizException(业务规则)</span></span>
<span class="line"><span> │ 唯一性校验抛出 BizException</span></span>
<span class="line"><span> │ DB::transaction 自动回滚</span></span>
<span class="line"><span> ▼ 向上抛出</span></span>
<span class="line"><span>Controller 层</span></span>
<span class="line"><span> │ 方式一:_add/_update/_delete 等快捷方法捕获 BizException → fail(消息, code)</span></span>
<span class="line"><span> │ 方式二:不捕获,由全局 Handler 统一处理</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Exceptions\\Handler::render()</span></span>
<span class="line"><span> │ BizException → {code:异常code(默认1), msg:&quot;业务提示&quot;}</span></span>
<span class="line"><span> │ ValidationException → {code:422, msg:&quot;参数验证失败&quot;, data:...}</span></span>
<span class="line"><span> │ AuthenticationException → {code:401}</span></span>
<span class="line"><span> │ NotFoundHttpException → {code:404}</span></span>
<span class="line"><span> │ 其它异常 → {code:500, msg:调试原文/生产&quot;操作失败&quot;}</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>_update</code> / <code>_delete</code> 调用时,<strong>不捕获异常</strong>,交给快捷方法与 Handler</li><li>Controller 手写复杂流程时,可 try-catch 捕获 <code>BizException</code> 后返回 <code>$this-&gt;fail()</code></li><li>Service 层的业务校验直接 <code>throw new BizException(&#39;提示&#39;)</code>,<strong>不要</strong>返回错误码让上层判断</li></ul><h2 id="完整请求示例" tabindex="-1">完整请求示例 <a class="header-anchor" href="#完整请求示例" aria-label="Permalink to &quot;完整请求示例&quot;">​</a></h2><p>以&quot;新增案例&quot;为例,展示一个请求从进入到返回的完整链路:</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/example/add</span></span>
<span class="line"><span>Content-Type: application/json</span></span>
<span class="line"><span>Authorization: Bearer &lt;token&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>{&quot;name&quot;: &quot;新案例&quot;, &quot;type&quot;: 1, &quot;status&quot;: 1}</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. ProxyMiddleware → 真实 IP 识别(受信代理)</span></span>
<span class="line"><span>2. Cors → 非 OPTIONS,放行(响应时追加 CORS 头)</span></span>
<span class="line"><span>3. ForceJsonResponse → Accept 强制 JSON</span></span>
<span class="line"><span>4. Authenticate → 校验 Token → 注入 attributes[&#39;userInfo&#39;]</span></span>
<span class="line"><span> → 反射 #[Permission] → checkPermission(&#39;sys:example:add&#39;) ✓</span></span>
<span class="line"><span>5. ResolveTenant → 注入 tenantId / isSuperAdmin</span></span>
<span class="line"><span>6. LogOperation(handle) → 收集上下文,读 #[OperationLog]</span></span>
<span class="line"><span></span></span>
<span class="line"><span>7. ExampleRequest</span></span>
<span class="line"><span> │ rules() 校验 name/type/status ✓</span></span>
<span class="line"><span> │ validated() → [&quot;name&quot;=&gt;&quot;新案例&quot;, &quot;type&quot;=&gt;1, &quot;status&quot;=&gt;1]</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>8. ExampleController::add()</span></span>
<span class="line"><span> │ 注解元数据已由 4/6 读取</span></span>
<span class="line"><span> │ $this-&gt;_add($service, $data)</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>9. ExampleService::add($data)</span></span>
<span class="line"><span> │ 入口键名转换 → array_camel_to_snake()</span></span>
<span class="line"><span> │ beforeAdd($data) → 默认 sort(钩子)</span></span>
<span class="line"><span> │ checkUnique($data) → name 唯一 ✓</span></span>
<span class="line"><span> │ DB::transaction {</span></span>
<span class="line"><span> │ Example::create($data) → creating:create_user/create_time 自动填充</span></span>
<span class="line"><span> │ }</span></span>
<span class="line"><span> │ afterAdd($id, $data) → 无额外操作</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>10. Database INSERT → laravel_example → 自增 ID 42</span></span>
<span class="line"><span></span></span>
<span class="line"><span>11. 返回 {&quot;code&quot;:0, &quot;ok&quot;:true, &quot;msg&quot;:&quot;添加成功&quot;, &quot;data&quot;:{&quot;id&quot;:42}}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>12. LogOperation(terminate) → 写入 laravel_operation_log(响应后,不影响业务)</span></span></code></pre></div><h2 id="层间通信规范" tabindex="-1">层间通信规范 <a class="header-anchor" href="#层间通信规范" aria-label="Permalink to &quot;层间通信规范&quot;">​</a></h2><table tabindex="0"><thead><tr><th>规则</th><th>说明</th></tr></thead><tbody><tr><td>Controller → Service</td><td>✅ 允许,标准调用</td></tr><tr><td>Controller → Model</td><td>❌ 禁止,越层</td></tr><tr><td>Controller → FormRequest</td><td>✅ 允许,类型注入</td></tr><tr><td>Service → Model</td><td>✅ 允许,标准调用</td></tr><tr><td>Service → 横切 Service</td><td>✅ 允许,调用通用能力(静态/注入均可)</td></tr><tr><td>Service → Controller</td><td>❌ 禁止,反向依赖</td></tr><tr><td>Service → FormRequest</td><td>❌ 禁止,格式校验在 HTTP 入口完成</td></tr><tr><td>Model → Service / Controller</td><td>❌ 禁止,反向依赖</td></tr><tr><td>Middleware → 后续链路</td><td>✅ 通过 <code>request-&gt;attributes</code> 注入数据</td></tr><tr><td>FormRequest → 业务规则</td><td>❌ 禁止,只做格式校验(业务规则在 Service)</td></tr></tbody></table>`,59)])])}const F=a(e,[["render",l]]);export{c as __pageData,F as default};