Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_EleVue/.vitepress/dist/assets/zh_architecture_base-classes.md.Byq0OFjk.lean.js
T
2026-10-25 18:52:36 +08:00

209 lines
69 KiB
JavaScript
Raw 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 t,a2 as e}from"./chunks/framework.B-QBKwc1.js";const o=JSON.parse('{"title":"核心基类体系","description":"项目有四个核心基类/骨架——Controller、BaseService、BaseTenantService、模型 Concerns Trait——构成\\"控制器 → 服务 → 模型\\"三层架构的骨架。","frontmatter":{},"headers":[],"relativePath":"zh/architecture/base-classes.md","filePath":"zh/architecture/base-classes.md"}'),n={name:"zh/architecture/base-classes.md"};function l(p,s,d,h,k,r){return i(),t("div",null,[...s[0]||(s[0]=[e(`<h1 id="_4-6-核心基类体系" tabindex="-1">4.6 核心基类体系 <a class="header-anchor" href="#_4-6-核心基类体系" aria-label="Permalink to &quot;4.6 核心基类体系&quot;">​</a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>项目有四个核心基类/骨架——<code>Controller</code>、<code>BaseService</code>、<code>BaseTenantService</code>、模型 Concerns Trait——构成&quot;控制器 → 服务 → 模型&quot;三层架构的骨架。所有业务模块都必须继承对应的基类,通过属性配置 + 生命周期钩子实现声明式 CRUD,子类只需声明&quot;做什么&quot;而不用写&quot;怎么做&quot;。</p></div><h2 id="基类继承关系" tabindex="-1">基类继承关系 <a class="header-anchor" href="#基类继承关系" aria-label="Permalink to &quot;基类继承关系&quot;">​</a></h2><div class="warning custom-block"><p class="custom-block-title">继承规则</p><ul><li>系统共享模块(菜单、字典、配置等)继承 <code>BaseService</code></li><li>租户隔离模块(用户、角色、文章、部门、职级等)继承 <code>BaseTenantService</code></li><li>所有模型继承 Eloquent <code>Model</code> 并组合 <code>SoftDeletesByFlag</code> + <code>HasAuditColumns</code> Trait</li><li>所有控制器继承 <code>App\\Http\\Controllers\\Controller</code></li><li>所有请求类继承 <code>BaseRequest</code></li></ul></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>Controller(控制器基类)</span></span>
<span class="line"><span> └── 所有 Api/Controller</span></span>
<span class="line"><span></span></span>
<span class="line"><span>BaseService(服务基类,组合 6 个 Concerns Trait 的占位)</span></span>
<span class="line"><span> ├── ExampleService(系统共享/示例)</span></span>
<span class="line"><span> ├── MenuService、DictCacheService、ConfigService ...</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> └── BaseTenantService(租户服务基类,use HasDataScope + tenantScopeField=&#39;tenant_id&#39;)</span></span>
<span class="line"><span> ├── UserService</span></span>
<span class="line"><span> ├── RoleService</span></span>
<span class="line"><span> ├── ArticleService</span></span>
<span class="line"><span> └── ...</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Illuminate\\Database\\Eloquent\\Model(Eloquent 模型)</span></span>
<span class="line"><span> └── 所有模型(use SoftDeletesByFlag, HasAuditColumns)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>FormRequest(Laravel 表单请求)</span></span>
<span class="line"><span> └── BaseRequest(统一失败响应)</span></span>
<span class="line"><span> └── 所有 Request</span></span></code></pre></div><h2 id="请求处理全链路" tabindex="-1">请求处理全链路 <a class="header-anchor" href="#请求处理全链路" aria-label="Permalink to &quot;请求处理全链路&quot;">​</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>HTTP 请求</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> │ FormRequest 类型注入 → 格式校验(validated())</span></span>
<span class="line"><span> │ success() / fail() / _page() / _add()</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>BaseService(业务编排、属性驱动)</span></span>
<span class="line"><span> │ 入口键名转换 → array_camel_to_snake()</span></span>
<span class="line"><span> │ beforeAdd() → 生命周期钩子</span></span>
<span class="line"><span> │ checkUnique() → 唯一性校验(BizException)</span></span>
<span class="line"><span> │ DB::transaction → 事务包裹</span></span>
<span class="line"><span> │ processAfterRead → 读取后处理链(文件/字典翻译)</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>Model(数据持久化,Concerns Trait)</span></span>
<span class="line"><span> │ HasAuditColumns → 审计字段自动填充(creating/updating 事件)</span></span>
<span class="line"><span> │ SoftDeleteFlagScope → 全局过滤 is_delete=1</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>数据库</span></span></code></pre></div><hr><h2 id="Controller" tabindex="-1">Controller <a class="header-anchor" href="#Controller" aria-label="Permalink to &quot;Controller&quot;">​</a></h2><p><strong>文件</strong>:<code>app/Http/Controllers/Controller.php</code></p><h3 id="设计要点" tabindex="-1">设计要点 <a class="header-anchor" href="#设计要点" aria-label="Permalink to &quot;设计要点&quot;">​</a></h3><ul><li>通用 CRUD 方法以 <code>_</code> 前缀命名,避免与子类路由方法冲突</li><li>子类通过<strong>构造函数注入</strong> Service(Laravel 依赖注入),无需 <code>initialize()</code></li><li>当前登录用户通过 <code>Support\\Auth</code> 工具类获取(<code>Auth::userId()</code> 等)</li><li><code>BizException</code> 由 <code>_add/_update/_delete</code> 等方法捕获转为失败响应</li></ul><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>┌─────────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│ Controller 基类 │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│ 统一响应(委托 Support\\Result) │</span></span>
<span class="line"><span>│ success($data, $msg, $raw) 成功响应 │</span></span>
<span class="line"><span>│ paginate($result) 分页响应 │</span></span>
<span class="line"><span>│ fail($msg, $code, $data) 失败响应(code 默认 1) │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│ 参数获取 │</span></span>
<span class="line"><span>│ getPageParams() 分页参数(pageNo≥1, │</span></span>
<span class="line"><span>│ pageSize 1~100,默认 20) │</span></span>
<span class="line"><span>│ getJsonBody() JSON 请求体 │</span></span>
<span class="line"><span>│ getParams() 全部参数(query + body 合并) │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│ 通用 CRUD(子类通过 $this-&gt; 调用) │</span></span>
<span class="line"><span>│ _page($service, $where, $with) 分页查询 │</span></span>
<span class="line"><span>│ _list($service, $where) 全量列表 │</span></span>
<span class="line"><span>│ _treeList($service, $where) 树形列表 │</span></span>
<span class="line"><span>│ _detail($service, $id) 详情(空 → 404) │</span></span>
<span class="line"><span>│ _add($service, $data) 新增(返回 id) │</span></span>
<span class="line"><span>│ _update($service, $id, $data) 修改 │</span></span>
<span class="line"><span>│ _delete($service, $id) 删除 │</span></span>
<span class="line"><span>│ _batchDelete($service, $ids) 批量删除 │</span></span>
<span class="line"><span>│ _export($service) 导出(返回文件地址) │</span></span>
<span class="line"><span>│ _import($service) 导入(文件校验 + 结果汇总) │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────┘</span></span></code></pre></div><h3 id="通用-CRUD-方法与-Service-层的对应关系" tabindex="-1">通用 CRUD 方法与 Service 层的对应关系 <a class="header-anchor" href="#通用-CRUD-方法与-Service-层的对应关系" aria-label="Permalink to &quot;通用 CRUD 方法与 Service 层的对应关系&quot;">​</a></h3><table tabindex="0"><thead><tr><th>Controller 方法</th><th>Service 方法</th><th>说明</th></tr></thead><tbody><tr><td><code>_page($service)</code></td><td><code>pageList()</code></td><td>分页查询</td></tr><tr><td><code>_list($service)</code></td><td><code>allList()</code></td><td>全量列表</td></tr><tr><td><code>_treeList($service)</code></td><td><code>treeList()</code></td><td>树形列表</td></tr><tr><td><code>_detail($service, $id)</code></td><td><code>detail($id)</code></td><td>详情查询(空返回 404)</td></tr><tr><td><code>_add($service, $data)</code></td><td><code>add($data)</code></td><td>新增(捕获 BizException)</td></tr><tr><td><code>_update($service, $id, $data)</code></td><td><code>update($id, $data)</code></td><td>修改(id 缺失直接失败提示)</td></tr><tr><td><code>_delete($service, $id)</code></td><td><code>delete($id)</code></td><td>删除(BizException→业务提示;其它异常记日志)</td></tr><tr><td><code>_batchDelete($service, $ids)</code></td><td><code>batchDelete($ids)</code></td><td>批量删除(ids 为空直接提示)</td></tr><tr><td><code>_export($service)</code></td><td><code>export()</code></td><td>导出</td></tr><tr><td><code>_import($service)</code></td><td><code>import()</code></td><td>导入(扩展名/大小/MIME 三重校验)</td></tr></tbody></table><h3 id="分页参数" tabindex="-1">分页参数 <a class="header-anchor" href="#分页参数" aria-label="Permalink to &quot;分页参数&quot;">​</a></h3><p><code>getPageParams()</code> 返回 <code>[&#39;pageNo&#39; =&gt; int, &#39;pageSize&#39; =&gt; int]</code>,带边界限制:</p><ul><li><code>pageNo</code> 最小为 1</li><li><code>pageSize</code> 限制在 1~100,默认 20(防止一次拉取过多数据)</li></ul><p>前端传参示例:<code>GET /api/example/page?pageNo=1&amp;pageSize=10&amp;name=测试&amp;status=1</code></p><h3 id="子类实现示例" tabindex="-1">子类实现示例 <a class="header-anchor" href="#子类实现示例" aria-label="Permalink to &quot;子类实现示例&quot;">​</a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> 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;"> // 分页查询 — 一行搞定</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:#7B7F8B;--shiki-light:#A0ADA0;"> // 详情</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:detail</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;"> detail</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">int</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">id</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;">_detail</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;">id</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;"> // 新增(参数由 ExampleRequest 类型注入校验)</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_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>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 删除 — 一行搞定</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:delete</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;"> delete</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">int</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">id</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;">_delete</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;">id</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><hr><h2 id="BaseService" tabindex="-1">BaseService <a class="header-anchor" href="#BaseService" aria-label="Permalink to &quot;BaseService&quot;">​</a></h2><p><strong>文件</strong>:<code>app/Services/BaseService.php</code></p><p>BaseService 是整个项目的核心,通过&quot;属性配置 + 生命周期钩子&quot;实现声明式 CRUD。子类只需配置属性和按需重写钩子,即可获得完整的增删改查能力。</p><h3 id="属性配置清单-全部集中声明于-BaseService-" tabindex="-1">属性配置清单(全部集中声明于 BaseService) <a class="header-anchor" href="#属性配置清单-全部集中声明于-BaseService-" aria-label="Permalink to &quot;属性配置清单(全部集中声明于 BaseService)&quot;">​</a></h3><table tabindex="0"><thead><tr><th>属性</th><th>类型</th><th>说明</th><th>示例</th></tr></thead><tbody><tr><td><code>modelClass</code></td><td>string</td><td>关联模型类名(<strong>必须</strong>)</td><td><code>Example::class</code></td></tr><tr><td><code>defaultWith</code></td><td>array</td><td>查询默认预加载关联</td><td><code>[&#39;creator&#39;]</code></td></tr><tr><td><code>fileFields</code></td><td>array</td><td>单文件字段列表(需 use HasFileProcessing)</td><td><code>[&#39;cover&#39;, &#39;avatar&#39;]</code></td></tr><tr><td><code>multiFileFields</code></td><td>array</td><td>多文件字段列表(逗号分隔的多个URL)</td><td><code>[&#39;images&#39;]</code></td></tr><tr><td><code>contentFields</code></td><td>array</td><td>富文本字段</td><td><code>[&#39;content&#39;]</code></td></tr><tr><td><code>fileSaveDir</code></td><td>string</td><td>文件保存子目录</td><td><code>&#39;article&#39;</code></td></tr><tr><td><code>pageLikeFields</code></td><td>array</td><td>LIKE 模糊匹配字段</td><td><code>[&#39;title&#39;, &#39;author&#39;]</code></td></tr><tr><td><code>pageEqFields</code></td><td>array</td><td>精确匹配字段</td><td><code>[&#39;status&#39;, &#39;category_id&#39;]</code></td></tr><tr><td><code>pageOrderBy</code></td><td>array</td><td>默认排序规则</td><td><code>[&#39;field&#39;=&gt;&#39;sort&#39;,&#39;type&#39;=&gt;&#39;asc&#39;]</code></td></tr><tr><td><code>orderFieldAllow</code></td><td>array</td><td>允许前端动态排序的字段白名单</td><td><code>[&#39;create_time&#39;]</code></td></tr><tr><td><code>uniqueFields</code></td><td>array</td><td>唯一性校验规则(字符串全局 / 数组分组)</td><td><code>[&#39;username&#39;, [&#39;code&#39;,&#39;pid&#39;]]</code></td></tr><tr><td><code>serializeMaps</code></td><td>array</td><td>字典显示名映射</td><td><code>[&#39;status&#39;=&gt;&#39;article_status&#39;]</code></td></tr><tr><td><code>dataScopeUserField</code></td><td>string</td><td>数据权限:归属字段(需 use HasDataScope)</td><td><code>&#39;create_user&#39;</code></td></tr><tr><td><code>dataScopeDeptField</code></td><td>string</td><td>数据权限:部门字段(需 use HasDataScope)</td><td><code>&#39;dept_id&#39;</code></td></tr><tr><td><code>tenantScopeField</code></td><td>string</td><td>租户隔离字段(BaseTenantService 预设)</td><td><code>&#39;tenant_id&#39;</code></td></tr><tr><td><code>treeParentField</code></td><td>string</td><td>树形结构父级字段</td><td><code>&#39;parent_id&#39;</code></td></tr><tr><td><code>treeLikeField</code></td><td>string</td><td>树形列表模糊搜索字段</td><td><code>&#39;name&#39;</code></td></tr></tbody></table><h3 id="生命周期钩子-HasCrudOperations-提供-10-个-" tabindex="-1">生命周期钩子(HasCrudOperations 提供 10 个) <a class="header-anchor" href="#生命周期钩子-HasCrudOperations-提供-10-个-" aria-label="Permalink to &quot;生命周期钩子(HasCrudOperations 提供 10 个)&quot;">​</a></h3><table tabindex="0"><thead><tr><th>钩子</th><th>时机</th><th>用途</th><th>可拦截</th></tr></thead><tbody><tr><td><code>beforeAdd($data)</code></td><td>新增前</td><td>修改数据、预处理,<strong>返回处理后的 data</strong></td><td>✅ 抛异常</td></tr><tr><td><code>afterAdd($id, $data)</code></td><td>新增后</td><td>保存关联表、发送通知</td><td>✅(事务内)</td></tr><tr><td><code>beforeUpdate($id, $data)</code></td><td>修改前</td><td>修改数据、预处理,<strong>返回处理后的 data</strong></td><td>✅ 抛异常</td></tr><tr><td><code>afterUpdate($id, $data)</code></td><td>修改后</td><td>更新关联表</td><td>✅(事务内)</td></tr><tr><td><code>beforeDelete($id)</code></td><td>删除前</td><td>检查子级、业务约束</td><td>✅ 抛异常</td></tr><tr><td><code>afterDelete($id)</code></td><td>删除后</td><td>清理关联数据</td><td>✅(事务内)</td></tr><tr><td><code>beforeBatchDelete($ids)</code></td><td>批量删除前</td><td>整体校验</td><td>✅ 抛异常</td></tr><tr><td><code>afterBatchDelete($ids)</code></td><td>批量删除后</td><td>批量清理</td><td>✅(事务内)</td></tr><tr><td><code>afterDetail($id, &amp;$data)</code></td><td>详情查询后</td><td>补充关联数据、格式化输出(引用传递)</td><td>❌</td></tr><tr><td><code>afterPageList(&amp;$records, $params)</code></td><td>列表查询后</td><td>批量补充关联数据,分页 pageList 与全量 allList 均触发(引用传递)</td><td>❌</td></tr></tbody></table><h3 id="新增流程-add-" tabindex="-1">新增流程(add) <a class="header-anchor" href="#新增流程-add-" aria-label="Permalink to &quot;新增流程(add)&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>array_camel_to_snake($data) ← 入口键名统一转为 snake_case</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>beforeAdd($data) ← 钩子:可修改数据,抛异常可拦截</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>checkUnique($data) ← 唯一性校验(uniqueFields,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> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span> Model::create($data) ← 写入数据库(HasAuditColumns 自动填充审计字段)</span></span>
<span class="line"><span> │</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> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>return $id</span></span></code></pre></div><div class="tip custom-block"><p class="custom-block-title">职责划分</p><p>参数格式校验<strong>不在 Service 内执行</strong>——由控制器的 FormRequest 在入口完成(见 <a href="/zh/guide/validator.html">3.4 FormRequest 验证器</a>);键名转换由 Service 写入入口(doAdd/doUpdate)统一完成。Service 只做业务规则(唯一性、状态依赖)。</p></div><h3 id="修改流程-update-" tabindex="-1">修改流程(update) <a class="header-anchor" href="#修改流程-update-" aria-label="Permalink to &quot;修改流程(update)&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>beforeUpdate($id, $data) ← 钩子</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>checkUnique($data, $excludeId) ← 唯一性校验(排除自身)</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> Model::find($id)-&gt;update($data) ← 更新(HasAuditColumns 强制刷新审计字段)</span></span>
<span class="line"><span> afterUpdate($id, $data) ← 钩子</span></span>
<span class="line"><span>}</span></span></code></pre></div><h3 id="删除流程-delete-" tabindex="-1">删除流程(delete) <a class="header-anchor" href="#删除流程-delete-" aria-label="Permalink to &quot;删除流程(delete)&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>beforeDelete($id) ← 钩子:抛异常可拦截(如存在关联数据)</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> is_delete = 1 ← 软删除(SoftDeletesByFlag)</span></span>
<span class="line"><span> afterDelete($id)</span></span>
<span class="line"><span>}</span></span></code></pre></div><h3 id="查询流程-pageList-allList-" tabindex="-1">查询流程(pageList / allList) <a class="header-anchor" href="#查询流程-pageList-allList-" aria-label="Permalink to &quot;查询流程(pageList / allList)&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>buildQuery($model) ← 构建查询条件(额外条件 + LIKE + 精确匹配</span></span>
<span class="line"><span> │ + 数据权限 + 租户隔离,camel→snake 转换参数)</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>applyOrderBy($model) ← 排序(前端 orderField/orderType 优先,</span></span>
<span class="line"><span> │ 白名单校验,否则用 pageOrderBy 配置)</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>$page / select ← 分页 / 全量查询</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>processAfterRead 处理链</span></span>
<span class="line"><span> ├─ processFileFieldsOnReadList ← 文件字段补全域名(HasFileProcessing 启用时)</span></span>
<span class="line"><span> ├─ processSerializeMapsList ← 字典显示名补全(status → statusText)</span></span>
<span class="line"><span> └─ filterFields ← 字段过滤(fields 参数指定返回字段)</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>afterPageList(&amp;$records) ← 钩子:批量补充关联数据(引用传递)</span></span></code></pre></div><h3 id="唯一性校验-uniqueFields-HasUniqueCheck-" tabindex="-1">唯一性校验(uniqueFields,HasUniqueCheck) <a class="header-anchor" href="#唯一性校验-uniqueFields-HasUniqueCheck-" aria-label="Permalink to &quot;唯一性校验(uniqueFields,HasUniqueCheck)&quot;">​</a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 全局唯一</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">uniqueFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">username</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;">mobile</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;">// 分组内唯一:code 在同一 pid 下唯一</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> array</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">uniqueFields</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> [</span></span>
<span class="line"><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">username</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 字符串 = 全局唯一</span></span>
<span class="line"><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;">code</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;">pid</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">],</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 数组 = [唯一字段, 分组字段],分组内唯一</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">];</span></span></code></pre></div><p>校验规则(<code>checkUnique</code>):</p><ul><li>add/update 时自动执行;值为空时跳过(FormRequest 的 <code>required</code> 兜底)</li><li>更新时自动排除自身(<code>excludeId</code>)</li><li>重复时抛出 <code>BizException(&quot;{字段中文名}已存在&quot;)</code></li><li>字段中文名查找顺序:<code>$fieldLabels</code> 属性 → Trait 内置映射 → 字段名本身 (子类声明 <code>protected array $fieldLabels = [&#39;code&#39; =&gt; &#39;角色编码&#39;];</code> 可自定义提示)</li></ul><h3 id="Trait-占位覆盖机制" tabindex="-1">Trait 占位覆盖机制 <a class="header-anchor" href="#Trait-占位覆盖机制" aria-label="Permalink to &quot;Trait 占位覆盖机制&quot;">​</a></h3><p><code>BaseService</code> 底部为 6 个 Trait 的所有方法提供<strong>占位实现</strong>(如 <code>processFileFieldsOnSave()</code> 直接 <code>return $data</code>)。子类 <code>use</code> 对应 Trait 后,PHP Trait 机制自动以真实实现覆盖占位——未启用的 Trait 方法保持空操作,避免调用未定义方法。</p><table tabindex="0"><thead><tr><th>Trait</th><th>提供的方法族</th></tr></thead><tbody><tr><td><code>HasCrudOperations</code></td><td>add / update / delete / batchDelete(事务 + 钩子)</td></tr><tr><td><code>HasQueryScopes</code></td><td>buildQuery / applyOrderBy / 排序白名单</td></tr><tr><td><code>HasFileProcessing</code></td><td>文件/多文件/富文本字段的保存迁移与读取补全</td></tr><tr><td><code>HasDataScope</code></td><td>applyDataScope / applyTenantScope / processTenantId</td></tr><tr><td><code>HasSerializeMaps</code></td><td>processSerializeMaps(字典翻译)</td></tr><tr><td><code>HasUniqueCheck</code></td><td>checkUnique / getFieldLabel</td></tr><tr><td><code>HasConfigValueProcessing</code></td><td>processConfigValueOnSave / buildConfigValueList / formatConfigValueOnRead</td></tr></tbody></table><p><code>HasConfigValueProcessing</code> 不依赖 <code>BaseService</code> 的占位实现,由 <code>ConfigService</code> 与 <code>ConfigItemService</code> 直接 <code>use</code>。</p><hr><h2 id="BaseTenantService" tabindex="-1">BaseTenantService <a class="header-anchor" href="#BaseTenantService" aria-label="Permalink to &quot;BaseTenantService&quot;">​</a></h2><p><strong>文件</strong>:<code>app/Services/BaseTenantService.php</code></p><h3 id="设计说明" tabindex="-1">设计说明 <a class="header-anchor" href="#设计说明" aria-label="Permalink to &quot;设计说明&quot;">​</a></h3><p>BaseTenantService 继承 BaseService,<strong>仅做两件事</strong>:<code>use HasDataScope</code> 引入数据权限 + 租户隔离能力,并将 <code>tenantScopeField</code> 预设为 <code>&#39;tenant_id&#39;</code>,子类自动获得租户隔离:</p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">abstract</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> BaseTenantService</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;"> Concerns</span><span style="--shiki-dark:#F286C4;--shiki-dark-font-style:italic;--shiki-light:#999999;--shiki-light-font-style:inherit;">\\</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">HasDataScope</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;"> * 预设为 tenant_id,子类如需使用其它字段名可覆盖。</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;">tenantScopeField</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;">tenant_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></code></pre></div><h3 id="隔离机制-由-HasDataScope-实现-" tabindex="-1">隔离机制(由 HasDataScope 实现) <a class="header-anchor" href="#隔离机制-由-HasDataScope-实现-" aria-label="Permalink to &quot;隔离机制(由 HasDataScope 实现)&quot;">​</a></h3><table tabindex="0"><thead><tr><th>场景</th><th>行为</th></tr></thead><tbody><tr><td><strong>查询时</strong></td><td><code>applyTenantScope</code> 自动叠加 <code>WHERE tenant_id = ?</code></td></tr><tr><td><strong>新增时</strong></td><td><code>processTenantId</code> 自动填充租户字段:平台用户代入 <code>config(&#39;tenant.default_tenant_id&#39;)</code>,租户用户代入当前 <code>tenantId</code></td></tr><tr><td><strong>超级管理员</strong></td><td><code>tenantId = 0</code>(平台用户)不过滤,可跨租户查看数据</td></tr></tbody></table><h3 id="租户上下文来源" tabindex="-1">租户上下文来源 <a class="header-anchor" href="#租户上下文来源" aria-label="Permalink to &quot;租户上下文来源&quot;">​</a></h3><p><code>ResolveTenant</code> 中间件解析并注入 <code>request-&gt;attributes[&#39;tenantId&#39;]</code>(详见 <a href="/zh/architecture/request-flow.html">4.2 请求处理链路</a>)。</p><h3 id="数据权限" tabindex="-1">数据权限 <a class="header-anchor" href="#数据权限" aria-label="Permalink to &quot;数据权限&quot;">​</a></h3><p><code>BaseTenantService</code> <strong>默认未启用数据权限</strong>,子类声明字段后 <code>HasDataScope</code> 才会叠加过滤(摘自 <code>app/Services/UserService.php</code> 实际代码):</p><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">class</span><span style="--shiki-dark:#97E1F1;--shiki-light:#2E8F82;"> UserService</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;"> BaseTenantService</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">dataScopeDeptField</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;">dept_id</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 启用按部门过滤</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="使用约定" tabindex="-1">使用约定 <a class="header-anchor" href="#使用约定" aria-label="Permalink to &quot;使用约定&quot;">​</a></h3><ul><li>需要租户隔离的模块继承此类(如 User、Article、Dept、Level 等)</li><li>系统共享表不继承此类,直接继承 BaseService(如 Menu、Dict、Config 等)</li></ul><hr><h2 id="模型层-Eloquent-Model-Concerns-Trait-" tabindex="-1">模型层(Eloquent Model + Concerns Trait) <a class="header-anchor" href="#模型层-Eloquent-Model-Concerns-Trait-" aria-label="Permalink to &quot;模型层(Eloquent Model + Concerns Trait)&quot;">​</a></h2><p><strong>文件</strong>:<code>app/Models/*.php</code> + <code>app/Models/Concerns/*.php</code></p><h3 id="设计说明-1" tabindex="-1">设计说明 <a class="header-anchor" href="#设计说明-1" aria-label="Permalink to &quot;设计说明&quot;">​</a></h3><p>本项目<strong>没有单一模型基类</strong>,模型直接继承 Eloquent <code>Model</code>,通过组合两个 Trait 获得横切能力:</p><ol><li><strong>软删除</strong>:<code>SoftDeletesByFlag</code>(基于 <code>is_delete</code> 字段,不使用 Laravel 内置 <code>SoftDeletes</code> 的 <code>deleted_at</code> 机制)</li><li><strong>审计字段自动写入</strong>:<code>HasAuditColumns</code>(<code>create_user</code> / <code>create_time</code> / <code>update_user</code> / <code>update_time</code>)</li></ol><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>┌─────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│ 软删除机制 │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 全局查询范围 SoftDeleteFlagScope: │</span></span>
<span class="line"><span>│ 所有查询自动叠加 WHERE is_delete = 0 │</span></span>
<span class="line"><span>│ → 已删除数据默认不可见 │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 软删除操作: │</span></span>
<span class="line"><span>│ $model-&gt;softDelete() → UPDATE SET is_delete = 1 │</span></span>
<span class="line"><span>│ Model::batchSoftDelete($ids) → 批量软删除 │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 查询已删除数据: │</span></span>
<span class="line"><span>│ Model::withoutGlobalScope(SoftDeleteFlagScope::class) │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────┘</span></span></code></pre></div><p><strong>方法列表:</strong></p><table tabindex="0"><thead><tr><th>方法</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>softDelete()</code></td><td>实例方法</td><td>设置 <code>is_delete = 1</code></td></tr><tr><td><code>batchSoftDelete($ids)</code></td><td>静态方法</td><td>批量软删除(先关闭全局范围再 whereIn 更新)</td></tr><tr><td><code>SoftDeleteFlagScope</code></td><td>全局范围</td><td>查询自动追加 <code>is_delete = 0</code></td></tr></tbody></table><h3 id="审计字段自动写入" tabindex="-1">审计字段自动写入 <a class="header-anchor" href="#审计字段自动写入" aria-label="Permalink to &quot;审计字段自动写入&quot;">​</a></h3><p>通过模型事件自动填充,<strong>无需手动处理</strong>:</p><table tabindex="0"><thead><tr><th>事件</th><th>自动填充</th><th>说明</th></tr></thead><tbody><tr><td><code>creating</code>(创建前)</td><td><code>create_user</code>、<code>create_time</code></td><td>调用方已显式传入则不覆盖(<code>??</code> 逻辑)</td></tr><tr><td><code>updating</code>(更新前)</td><td><code>update_user</code>、<code>update_time</code></td><td>每次强制覆盖</td></tr></tbody></table><p>用户来源:<code>Auth::username()</code>(当前登录用户,来自 <code>auth.jwt</code> 中间件注入的 <code>userInfo</code>)。无登录用户(如队列/命令行)时用户字段保留空值,时间字段仍写入。</p><h3 id="数据库字段规范" tabindex="-1">数据库字段规范 <a class="header-anchor" href="#数据库字段规范" aria-label="Permalink to &quot;数据库字段规范&quot;">​</a></h3><p>所有表建议包含以下公共字段:</p><table tabindex="0"><thead><tr><th>字段名</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>id</code></td><td>int</td><td>主键,自增</td></tr><tr><td><code>is_delete</code></td><td>tinyint(1)</td><td>软删除标记,0=正常 1=已删除</td></tr><tr><td><code>create_user</code></td><td>varchar(50)</td><td>创建人</td></tr><tr><td><code>create_time</code></td><td>datetime</td><td>创建时间</td></tr><tr><td><code>update_user</code></td><td>varchar(50)</td><td>更新人</td></tr><tr><td><code>update_time</code></td><td>datetime</td><td>更新时间</td></tr></tbody></table><h3 id="使用示例" tabindex="-1">使用示例 <a class="header-anchor" href="#使用示例" aria-label="Permalink to &quot;使用示例&quot;">​</a></h3><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 模型定义 — 最简形式</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">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:#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:#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:#F286C4;--shiki-light:#AB5959;"> protected</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> $</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">fillable</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 style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">avatar</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;">type</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;">status</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;">sort</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>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 软删除</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">$</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">example</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:#62E884;--shiki-light:#59873A;">find</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;">1</span><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;">example</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">-&gt;</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">softDelete</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">();</span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // UPDATE laravel_example SET is_delete = 1 WHERE id = 1</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:#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:#62E884;--shiki-light:#59873A;">batchSoftDelete</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">([</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;">1</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 2</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 3</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:#F6F6F4;--shiki-light:#999999;">$</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">all</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:#62E884;--shiki-light:#59873A;">withoutGlobalScope</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;">SoftDeleteFlagScope</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:#F286C4;--shiki-light:#AB5959;">-&gt;</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">get</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:#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:#62E884;--shiki-light:#59873A;">create</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:#F286C4;--shiki-light:#AB5959;"> =&gt;</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>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 自动填充: create_user=&#39;admin&#39;, create_time=&#39;2026-10-02 12:00:00&#39;</span></span></code></pre></div><hr><h2 id="BaseRequest-表单请求基类-" tabindex="-1">BaseRequest(表单请求基类) <a class="header-anchor" href="#BaseRequest-表单请求基类-" aria-label="Permalink to &quot;BaseRequest(表单请求基类)&quot;">​</a></h2><p><strong>文件</strong>:<code>app/Http/Requests/BaseRequest.php</code></p><table tabindex="0"><thead><tr><th>能力</th><th>说明</th></tr></thead><tbody><tr><td><code>authorize()</code></td><td>统一返回 true(鉴权由中间件与 <code>#[Permission]</code> 负责)</td></tr><tr><td><code>failedValidation()</code></td><td>统一返回 <code>code=1</code> + 字段错误明细的 JSON</td></tr></tbody></table><p>详见 <a href="/zh/guide/validator.html">3.4 FormRequest 验证器</a>。</p><hr><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><th>原因</th></tr></thead><tbody><tr><td>系统级共享数据(菜单、字典、配置)</td><td><code>BaseService</code></td><td>不需要租户隔离</td></tr><tr><td>租户业务数据(用户、角色、文章、部门、职级)</td><td><code>BaseTenantService</code></td><td>自动 <code>tenant_id</code> 隔离</td></tr><tr><td>所有数据模型</td><td><code>Model</code> + 双 Trait</td><td>软删除 + 审计字段</td></tr><tr><td>所有控制器</td><td><code>Controller</code></td><td>统一响应 + 通用 CRUD</td></tr><tr><td>所有表单请求</td><td><code>BaseRequest</code></td><td>统一失败响应 + 键名转换</td></tr></tbody></table><div class="tip custom-block"><p class="custom-block-title">快速判断</p><p>问自己:这个模块的数据是否需要按租户隔离?</p><ul><li><strong>是</strong> → 继承 <code>BaseTenantService</code></li><li><strong>否</strong> → 继承 <code>BaseService</code></li></ul><p>文件/富文本/数据权限等能力按需 <code>use</code> 对应 Trait,未启用时占位方法自动空操作。</p></div>`,91)])])}const g=a(n,[["render",l]]);export{o as __pageData,g as default};