<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>WEMIRR-PLATFORM</title>
        <link>https://docs.battcn.com/</link>
        <description>一站式微服务内容平台，包括学习路线、知识体系，海量面试问题解答，一站式阅读体验，跟随前沿技术，深度和广度学习，Spring Boot 和 Spring Cloud 生态。</description>
        <lastBuildDate>Sat, 14 Feb 2026 02:48:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <image>
            <title>WEMIRR-PLATFORM</title>
            <url>https://docs.battcn.com/logo.png</url>
            <link>https://docs.battcn.com/</link>
        </image>
        <copyright>Copyright (c) 2022-present, Chocolate and WEMIRR-PLATFORM contributors</copyright>
        <item>
            <title><![CDATA[无题]]></title>
            <link>https://docs.battcn.com/guide/test.html</link>
            <guid>https://docs.battcn.com/guide/test.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<p>12222222222222222222
12321
3
21
3
21
3</p>
<p>21
3
21
fdsf
sdf
sdf
sdf
sf</p>
]]></description>
            <content:encoded><![CDATA[<p>12222222222222222222
12321
3
21
3
21
3</p>
<p>21
3
21
fdsf
sdf
sdf
sdf
sf</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WEMIRR PLATFORM]]></title>
            <link>https://docs.battcn.com/</link>
            <guid>https://docs.battcn.com/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[成为赞助者 ]]></title>
            <link>https://docs.battcn.com/zh/becoming-a-sponsor.html</link>
            <guid>https://docs.battcn.com/zh/becoming-a-sponsor.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="成为赞助者" tabindex="-1">成为赞助者 <a class="header-anchor" href="#成为赞助者" aria-label="Permalink to &quot;成为赞助者&quot;">&ZeroWidthSpace;</a></h1>
<p><code>WEMIRR-PLATFORM</code> 是一个优雅的微服务中台框架，由于其友好的 API 设计和出色的性能，在极短的时间内被大众所知。 我们为此投入了大量的时间和无限的热爱。</p>
<p>由于 <code>WEMIRR-PLATFORM</code> 基于 Apache 开源协议，任何个人、企业和机构免费商用，但 <code>WEMIRR-PLATFORM</code> 的开发、维护和网站服务器的开支，对于我们的热爱造成了许多阻碍， 为此，我们寻求赞助，也为您的产品或品牌提供一个通过
<code>WEMIRR-PLATFORM</code> 展示的机会。</p>
<h2 id="展示位赞助" tabindex="-1">展示位赞助 <a class="header-anchor" href="#展示位赞助" aria-label="Permalink to &quot;展示位赞助&quot;">&ZeroWidthSpace;</a></h2>
<p>在本站对应的位置放置您的 LOGO 及跳转链接。</p>
<table tabindex="0">
<thead>
<tr>
<th>位置</th>
<th>素材</th>
<th>宽高</th>
<th>赞助费</th>
<th>名额</th>
</tr>
</thead>
<tbody>
<tr>
<td>全站右侧边栏 1</td>
<td>图片+链接</td>
<td>105*50px</td>
<td>￥200/月</td>
<td>8</td>
</tr>
<tr>
<td>全站右侧边栏 2</td>
<td>图片+链接</td>
<td>122*50px</td>
<td>￥400/月</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>您可以联系微信 <code>battcn2022</code> 就展示位的详细事宜。</p>
<h2 id="无偿捐赠" tabindex="-1">无偿捐赠 <a class="header-anchor" href="#无偿捐赠" aria-label="Permalink to &quot;无偿捐赠&quot;">&ZeroWidthSpace;</a></h2>
<p>您也可以通过 Gitee 平台为 <code>WEMIRR-PLATFORM</code> 提供无偿捐赠（请量力而行）：</p>
<a href="https://gitee.com/battcn/wemirr-platform?donate=true" class="VPButton medium brand" target="_blank">
    <img src="https://gitee.com/static/images/logo.svg" style="width: 80px;margin: 10px 0 -5px 6px" />
    通过 Gitee 捐赠
</a>
]]></description>
            <content:encoded><![CDATA[<h1 id="成为赞助者" tabindex="-1">成为赞助者 <a class="header-anchor" href="#成为赞助者" aria-label="Permalink to &quot;成为赞助者&quot;">&ZeroWidthSpace;</a></h1>
<p><code>WEMIRR-PLATFORM</code> 是一个优雅的微服务中台框架，由于其友好的 API 设计和出色的性能，在极短的时间内被大众所知。 我们为此投入了大量的时间和无限的热爱。</p>
<p>由于 <code>WEMIRR-PLATFORM</code> 基于 Apache 开源协议，任何个人、企业和机构免费商用，但 <code>WEMIRR-PLATFORM</code> 的开发、维护和网站服务器的开支，对于我们的热爱造成了许多阻碍， 为此，我们寻求赞助，也为您的产品或品牌提供一个通过
<code>WEMIRR-PLATFORM</code> 展示的机会。</p>
<h2 id="展示位赞助" tabindex="-1">展示位赞助 <a class="header-anchor" href="#展示位赞助" aria-label="Permalink to &quot;展示位赞助&quot;">&ZeroWidthSpace;</a></h2>
<p>在本站对应的位置放置您的 LOGO 及跳转链接。</p>
<table tabindex="0">
<thead>
<tr>
<th>位置</th>
<th>素材</th>
<th>宽高</th>
<th>赞助费</th>
<th>名额</th>
</tr>
</thead>
<tbody>
<tr>
<td>全站右侧边栏 1</td>
<td>图片+链接</td>
<td>105*50px</td>
<td>￥200/月</td>
<td>8</td>
</tr>
<tr>
<td>全站右侧边栏 2</td>
<td>图片+链接</td>
<td>122*50px</td>
<td>￥400/月</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>您可以联系微信 <code>battcn2022</code> 就展示位的详细事宜。</p>
<h2 id="无偿捐赠" tabindex="-1">无偿捐赠 <a class="header-anchor" href="#无偿捐赠" aria-label="Permalink to &quot;无偿捐赠&quot;">&ZeroWidthSpace;</a></h2>
<p>您也可以通过 Gitee 平台为 <code>WEMIRR-PLATFORM</code> 提供无偿捐赠（请量力而行）：</p>
<a href="https://gitee.com/battcn/wemirr-platform?donate=true" class="VPButton medium brand" target="_blank">
    <img src="https://gitee.com/static/images/logo.svg" style="width: 80px;margin: 10px 0 -5px 6px" />
    通过 Gitee 捐赠
</a>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[插件市场 ]]></title>
            <link>https://docs.battcn.com/zh/business/</link>
            <guid>https://docs.battcn.com/zh/business/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="插件市场" tabindex="-1">插件市场 <a class="header-anchor" href="#插件市场" aria-label="Permalink to &quot;插件市场&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p><strong>无商业版：接需求定制开发,为身边小伙伴某生存</strong></p>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="插件市场" tabindex="-1">插件市场 <a class="header-anchor" href="#插件市场" aria-label="Permalink to &quot;插件市场&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p><strong>无商业版：接需求定制开发,为身边小伙伴某生存</strong></p>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[流程监听器说明 ]]></title>
            <link>https://docs.battcn.com/zh/business/listener.html</link>
            <guid>https://docs.battcn.com/zh/business/listener.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="流程监听器说明" tabindex="-1">流程监听器说明 <a class="header-anchor" href="#流程监听器说明" aria-label="Permalink to &quot;流程监听器说明&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">Camunda BPM 流程监听器</p>
<p>监听器分为ExecutionListener【执行监听器】和TaskListener【任务监听器】，我们知道不管是连线还是任务节点底层都有对应的执行器，所以ExecutionListener是可以设置在连线和节点上的。</p>
</div>
<h2 id="执行监听器" tabindex="-1">执行监听器 <a class="header-anchor" href="#执行监听器" aria-label="Permalink to &quot;执行监听器&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">执行监听器说明</p>
<ul>
<li>执行监听器一共有 <code>start</code>, <code>end</code>, <code>take</code> 三种触发事件</li>
<li>其中 <code>take</code> 事件只有是连线才存在的</li>
</ul>
</div>
<h2 id="任务监听器" tabindex="-1">任务监听器 <a class="header-anchor" href="#任务监听器" aria-label="Permalink to &quot;任务监听器&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">任务监听器说明</p>
<ul>
<li>任务监听器一共有 <code>create</code>, <code>assignment</code>, <code>update</code>, <code>complete</code>, <code>delete</code>, <code>timeout</code> 六种触发事件</li>
<li>其中常用的有 <code>create</code>, <code>assignment</code>, <code>complete</code></li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="流程监听器说明" tabindex="-1">流程监听器说明 <a class="header-anchor" href="#流程监听器说明" aria-label="Permalink to &quot;流程监听器说明&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">Camunda BPM 流程监听器</p>
<p>监听器分为ExecutionListener【执行监听器】和TaskListener【任务监听器】，我们知道不管是连线还是任务节点底层都有对应的执行器，所以ExecutionListener是可以设置在连线和节点上的。</p>
</div>
<h2 id="执行监听器" tabindex="-1">执行监听器 <a class="header-anchor" href="#执行监听器" aria-label="Permalink to &quot;执行监听器&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">执行监听器说明</p>
<ul>
<li>执行监听器一共有 <code>start</code>, <code>end</code>, <code>take</code> 三种触发事件</li>
<li>其中 <code>take</code> 事件只有是连线才存在的</li>
</ul>
</div>
<h2 id="任务监听器" tabindex="-1">任务监听器 <a class="header-anchor" href="#任务监听器" aria-label="Permalink to &quot;任务监听器&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">任务监听器说明</p>
<ul>
<li>任务监听器一共有 <code>create</code>, <code>assignment</code>, <code>update</code>, <code>complete</code>, <code>delete</code>, <code>timeout</code> 六种触发事件</li>
<li>其中常用的有 <code>create</code>, <code>assignment</code>, <code>complete</code></li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[运输管理插件（TMS） ]]></title>
            <link>https://docs.battcn.com/zh/business/tms.html</link>
            <guid>https://docs.battcn.com/zh/business/tms.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="运输管理插件-tms" tabindex="-1">运输管理插件（TMS） <a class="header-anchor" href="#运输管理插件-tms" aria-label="Permalink to &quot;运输管理插件（TMS）&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">核心信息</p>
<ul>
<li><strong>全称</strong>：Transportation Management System</li>
<li><strong>服务</strong>：<code>wemirr-platform-tms</code></li>
<li><strong>端口</strong>：<code>5005</code></li>
</ul>
</div>
<hr>
<h2 id="概念介绍" tabindex="-1">概念介绍 <a class="header-anchor" href="#概念介绍" aria-label="Permalink to &quot;概念介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="什么是-tms" tabindex="-1">什么是 TMS？ <a class="header-anchor" href="#什么是-tms" aria-label="Permalink to &quot;什么是 TMS？&quot;">&ZeroWidthSpace;</a></h3>
<p>TMS 是运输管理系统，用于管理物流运输全流程，从车辆调度到费用结算。</p>
<p><strong>核心价值</strong>：提升运输效率、降低物流成本、实现运输可视化。</p>
<h3 id="业务架构" tabindex="-1">业务架构 <a class="header-anchor" href="#业务架构" aria-label="Permalink to &quot;业务架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│   ┌───────────┐    ┌───────────┐    ┌───────────┐     │</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>│   ┌─────────────────────────────────────────────────┐  │</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></code></pre>
</div><hr>
<h2 id="功能模块" tabindex="-1">功能模块 <a class="header-anchor" href="#功能模块" aria-label="Permalink to &quot;功能模块&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="资源管理" tabindex="-1">资源管理 <a class="header-anchor" href="#资源管理" aria-label="Permalink to &quot;资源管理&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>司机管理</td>
<td>司机信息、证件、状态管理</td>
</tr>
<tr>
<td>车辆管理</td>
<td>车辆信息、保险、年检管理</td>
</tr>
<tr>
<td>车队管理</td>
<td>车队组织、司机车辆分配</td>
</tr>
</tbody>
</table>
<h3 id="订单管理" tabindex="-1">订单管理 <a class="header-anchor" href="#订单管理" aria-label="Permalink to &quot;订单管理&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>订单创建</td>
<td>录入运输任务、货物信息</td>
</tr>
<tr>
<td>调度分配</td>
<td>分配司机、车辆</td>
</tr>
<tr>
<td>运输跟踪</td>
<td>实时查看运输状态</td>
</tr>
</tbody>
</table>
<h3 id="车辆维护" tabindex="-1">车辆维护 <a class="header-anchor" href="#车辆维护" aria-label="Permalink to &quot;车辆维护&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>事故记录</td>
<td>记录车辆事故及处理情况</td>
</tr>
<tr>
<td>违章记录</td>
<td>记录车辆违章及罚款</td>
</tr>
<tr>
<td>规费管理</td>
<td>保险、年检、路桥费等</td>
</tr>
<tr>
<td>维修保养</td>
<td>维修记录、保养计划</td>
</tr>
</tbody>
</table>
<h3 id="结算管理" tabindex="-1">结算管理 <a class="header-anchor" href="#结算管理" aria-label="Permalink to &quot;结算管理&quot;">&ZeroWidthSpace;</a></h3>
<p>支持自定义计费规则，根据重量、里程、体积等自动计算运费。</p>
<hr>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1️⃣-启动服务" tabindex="-1">1️⃣ 启动服务 <a class="header-anchor" href="#_1️⃣-启动服务" aria-label="Permalink to &quot;1️⃣ 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># IDEA 中运行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">TmsApplication.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或命令行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> spring-boot:run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -pl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-plugin/wemirr-platform-tms</span></span></code></pre>
</div><h3 id="_2️⃣-初始化资源" tabindex="-1">2️⃣ 初始化资源 <a class="header-anchor" href="#_2️⃣-初始化资源" aria-label="Permalink to &quot;2️⃣ 初始化资源&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>创建车队 → 添加车辆 → 添加司机 → 审核通过</span></span></code></pre>
</div><h3 id="_3️⃣-创建订单" tabindex="-1">3️⃣ 创建订单 <a class="header-anchor" href="#_3️⃣-创建订单" aria-label="Permalink to &quot;3️⃣ 创建订单&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【订单管理】创建运输订单，分配车辆和司机。</p>
<h3 id="_4️⃣-配置结算" tabindex="-1">4️⃣ 配置结算 <a class="header-anchor" href="#_4️⃣-配置结算" aria-label="Permalink to &quot;4️⃣ 配置结算&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【结算管理】配置计费规则，系统自动计算运费。</p>
<hr>
<h2 id="业务流程" tabindex="-1">业务流程 <a class="header-anchor" href="#业务流程" aria-label="Permalink to &quot;业务流程&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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></code></pre>
</div><hr>
<h2 id="注意事项" tabindex="-1">注意事项 <a class="header-anchor" href="#注意事项" aria-label="Permalink to &quot;注意事项&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">审核机制</p>
<p>新建的司机和车辆默认为 <strong>待审核</strong> 状态，需管理员审核通过后方可用于订单分配。</p>
</div>
<hr>
<h2 id="相关链接" tabindex="-1">相关链接 <a class="header-anchor" href="#相关链接" aria-label="Permalink to &quot;相关链接&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/business/workflow.html">工作流插件</a></li>
<li><a href="/zh/business/wms.html">仓储管理（WMS）</a></li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="运输管理插件-tms" tabindex="-1">运输管理插件（TMS） <a class="header-anchor" href="#运输管理插件-tms" aria-label="Permalink to &quot;运输管理插件（TMS）&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">核心信息</p>
<ul>
<li><strong>全称</strong>：Transportation Management System</li>
<li><strong>服务</strong>：<code>wemirr-platform-tms</code></li>
<li><strong>端口</strong>：<code>5005</code></li>
</ul>
</div>
<hr>
<h2 id="概念介绍" tabindex="-1">概念介绍 <a class="header-anchor" href="#概念介绍" aria-label="Permalink to &quot;概念介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="什么是-tms" tabindex="-1">什么是 TMS？ <a class="header-anchor" href="#什么是-tms" aria-label="Permalink to &quot;什么是 TMS？&quot;">&ZeroWidthSpace;</a></h3>
<p>TMS 是运输管理系统，用于管理物流运输全流程，从车辆调度到费用结算。</p>
<p><strong>核心价值</strong>：提升运输效率、降低物流成本、实现运输可视化。</p>
<h3 id="业务架构" tabindex="-1">业务架构 <a class="header-anchor" href="#业务架构" aria-label="Permalink to &quot;业务架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│   ┌───────────┐    ┌───────────┐    ┌───────────┐     │</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>│   ┌─────────────────────────────────────────────────┐  │</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></code></pre>
</div><hr>
<h2 id="功能模块" tabindex="-1">功能模块 <a class="header-anchor" href="#功能模块" aria-label="Permalink to &quot;功能模块&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="资源管理" tabindex="-1">资源管理 <a class="header-anchor" href="#资源管理" aria-label="Permalink to &quot;资源管理&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>司机管理</td>
<td>司机信息、证件、状态管理</td>
</tr>
<tr>
<td>车辆管理</td>
<td>车辆信息、保险、年检管理</td>
</tr>
<tr>
<td>车队管理</td>
<td>车队组织、司机车辆分配</td>
</tr>
</tbody>
</table>
<h3 id="订单管理" tabindex="-1">订单管理 <a class="header-anchor" href="#订单管理" aria-label="Permalink to &quot;订单管理&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>订单创建</td>
<td>录入运输任务、货物信息</td>
</tr>
<tr>
<td>调度分配</td>
<td>分配司机、车辆</td>
</tr>
<tr>
<td>运输跟踪</td>
<td>实时查看运输状态</td>
</tr>
</tbody>
</table>
<h3 id="车辆维护" tabindex="-1">车辆维护 <a class="header-anchor" href="#车辆维护" aria-label="Permalink to &quot;车辆维护&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>事故记录</td>
<td>记录车辆事故及处理情况</td>
</tr>
<tr>
<td>违章记录</td>
<td>记录车辆违章及罚款</td>
</tr>
<tr>
<td>规费管理</td>
<td>保险、年检、路桥费等</td>
</tr>
<tr>
<td>维修保养</td>
<td>维修记录、保养计划</td>
</tr>
</tbody>
</table>
<h3 id="结算管理" tabindex="-1">结算管理 <a class="header-anchor" href="#结算管理" aria-label="Permalink to &quot;结算管理&quot;">&ZeroWidthSpace;</a></h3>
<p>支持自定义计费规则，根据重量、里程、体积等自动计算运费。</p>
<hr>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1️⃣-启动服务" tabindex="-1">1️⃣ 启动服务 <a class="header-anchor" href="#_1️⃣-启动服务" aria-label="Permalink to &quot;1️⃣ 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># IDEA 中运行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">TmsApplication.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或命令行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> spring-boot:run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -pl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-plugin/wemirr-platform-tms</span></span></code></pre>
</div><h3 id="_2️⃣-初始化资源" tabindex="-1">2️⃣ 初始化资源 <a class="header-anchor" href="#_2️⃣-初始化资源" aria-label="Permalink to &quot;2️⃣ 初始化资源&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>创建车队 → 添加车辆 → 添加司机 → 审核通过</span></span></code></pre>
</div><h3 id="_3️⃣-创建订单" tabindex="-1">3️⃣ 创建订单 <a class="header-anchor" href="#_3️⃣-创建订单" aria-label="Permalink to &quot;3️⃣ 创建订单&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【订单管理】创建运输订单，分配车辆和司机。</p>
<h3 id="_4️⃣-配置结算" tabindex="-1">4️⃣ 配置结算 <a class="header-anchor" href="#_4️⃣-配置结算" aria-label="Permalink to &quot;4️⃣ 配置结算&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【结算管理】配置计费规则，系统自动计算运费。</p>
<hr>
<h2 id="业务流程" tabindex="-1">业务流程 <a class="header-anchor" href="#业务流程" aria-label="Permalink to &quot;业务流程&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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></code></pre>
</div><hr>
<h2 id="注意事项" tabindex="-1">注意事项 <a class="header-anchor" href="#注意事项" aria-label="Permalink to &quot;注意事项&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">审核机制</p>
<p>新建的司机和车辆默认为 <strong>待审核</strong> 状态，需管理员审核通过后方可用于订单分配。</p>
</div>
<hr>
<h2 id="相关链接" tabindex="-1">相关链接 <a class="header-anchor" href="#相关链接" aria-label="Permalink to &quot;相关链接&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/business/workflow.html">工作流插件</a></li>
<li><a href="/zh/business/wms.html">仓储管理（WMS）</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[仓储管理插件（WMS） ]]></title>
            <link>https://docs.battcn.com/zh/business/wms.html</link>
            <guid>https://docs.battcn.com/zh/business/wms.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="仓储管理插件-wms" tabindex="-1">仓储管理插件（WMS） <a class="header-anchor" href="#仓储管理插件-wms" aria-label="Permalink to &quot;仓储管理插件（WMS）&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">核心信息</p>
<ul>
<li><strong>全称</strong>：Warehouse Management System</li>
<li><strong>服务</strong>：<code>wemirr-platform-wms</code></li>
<li><strong>端口</strong>：<code>5006</code></li>
</ul>
</div>
<hr>
<h2 id="概念介绍" tabindex="-1">概念介绍 <a class="header-anchor" href="#概念介绍" aria-label="Permalink to &quot;概念介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="什么是-wms" tabindex="-1">什么是 WMS？ <a class="header-anchor" href="#什么是-wms" aria-label="Permalink to &quot;什么是 WMS？&quot;">&ZeroWidthSpace;</a></h3>
<p>WMS 是仓储管理系统，用于管理仓库内货物的存储、流转和作业全过程。</p>
<p><strong>核心价值</strong>：提升仓库空间利用率、优化作业效率、实现库存精准管理。</p>
<h3 id="仓库层级结构" tabindex="-1">仓库层级结构 <a class="header-anchor" href="#仓库层级结构" aria-label="Permalink to &quot;仓库层级结构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                      仓库 Warehouse                      │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│                                                         │</span></span>
<span class="line"><span>│   ┌─────────────────────────────────────────────────┐  │</span></span>
<span class="line"><span>│   │              库区 Storage Area                   │  │</span></span>
<span class="line"><span>│   │  ┌─────────────────────────────────────────┐    │  │</span></span>
<span class="line"><span>│   │  │           巷道 Aisle                     │    │  │</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>│                                                         │</span></span>
<span class="line"><span>│   ┌──────────┐                      ┌──────────┐       │</span></span>
<span class="line"><span>│   │  月台    │                      │  工作台   │       │</span></span>
<span class="line"><span>│   │  Dock    │                      │ Workbench │       │</span></span>
<span class="line"><span>│   └──────────┘                      └──────────┘       │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────┘</span></span></code></pre>
</div><h3 id="层级说明" tabindex="-1">层级说明 <a class="header-anchor" href="#层级说明" aria-label="Permalink to &quot;层级说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>层级</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>仓库</strong></td>
<td>顶层管理单元，代表物理仓库</td>
</tr>
<tr>
<td><strong>库区</strong></td>
<td>按功能划分（收货区、存储区、发货区）</td>
</tr>
<tr>
<td><strong>巷道</strong></td>
<td>库区内的通道</td>
</tr>
<tr>
<td><strong>储位</strong></td>
<td>存放货物的最小位置单元</td>
</tr>
<tr>
<td><strong>容器</strong></td>
<td>存放货物的载体（托盘、周转箱）</td>
</tr>
<tr>
<td><strong>月台</strong></td>
<td>装卸货平台</td>
</tr>
<tr>
<td><strong>工作台</strong></td>
<td>操作人员工作站点</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="功能模块" tabindex="-1">功能模块 <a class="header-anchor" href="#功能模块" aria-label="Permalink to &quot;功能模块&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础数据" tabindex="-1">基础数据 <a class="header-anchor" href="#基础数据" aria-label="Permalink to &quot;基础数据&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>仓库管理</td>
<td>仓库基本信息配置</td>
</tr>
<tr>
<td>库区管理</td>
<td>划分存储区域</td>
</tr>
<tr>
<td>储位管理</td>
<td>定义存储位置</td>
</tr>
<tr>
<td>容器管理</td>
<td>管理托盘、周转箱等</td>
</tr>
</tbody>
</table>
<h3 id="仓库作业" tabindex="-1">仓库作业 <a class="header-anchor" href="#仓库作业" aria-label="Permalink to &quot;仓库作业&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>入库管理</td>
<td>收货、质检、上架</td>
</tr>
<tr>
<td>出库管理</td>
<td>拣货、复核、发货</td>
</tr>
<tr>
<td>库内作业</td>
<td>移库、盘点、调整</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1️⃣-启动服务" tabindex="-1">1️⃣ 启动服务 <a class="header-anchor" href="#_1️⃣-启动服务" aria-label="Permalink to &quot;1️⃣ 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># IDEA 中运行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">WmsApplication.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或命令行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> spring-boot:run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -pl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-plugin/wemirr-platform-wms</span></span></code></pre>
</div><h3 id="_2️⃣-初始化仓库" tabindex="-1">2️⃣ 初始化仓库 <a class="header-anchor" href="#_2️⃣-初始化仓库" aria-label="Permalink to &quot;2️⃣ 初始化仓库&quot;">&ZeroWidthSpace;</a></h3>
<p>按层级依次创建：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>仓库 → 库区 → 巷道 → 储位 → 容器</span></span></code></pre>
</div><h3 id="_3️⃣-开始作业" tabindex="-1">3️⃣ 开始作业 <a class="header-anchor" href="#_3️⃣-开始作业" aria-label="Permalink to &quot;3️⃣ 开始作业&quot;">&ZeroWidthSpace;</a></h3>
<p>配置完成后即可进行入库、出库等仓储作业。</p>
<hr>
<h2 id="业务流程" tabindex="-1">业务流程 <a class="header-anchor" href="#业务流程" aria-label="Permalink to &quot;业务流程&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="入库流程" tabindex="-1">入库流程 <a class="header-anchor" href="#入库流程" aria-label="Permalink to &quot;入库流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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></code></pre>
</div><h3 id="出库流程" tabindex="-1">出库流程 <a class="header-anchor" href="#出库流程" aria-label="Permalink to &quot;出库流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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></code></pre>
</div><hr>
<h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="储位编码规范" tabindex="-1">储位编码规范 <a class="header-anchor" href="#储位编码规范" aria-label="Permalink to &quot;储位编码规范&quot;">&ZeroWidthSpace;</a></h3>
<p>建议采用层级编码，便于快速定位：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>格式：仓库-库区-巷道-层-列</span></span>
<span class="line"><span>示例：WH01-A01-01-03-05</span></span>
<span class="line"><span>       │    │    │   │   └── 第5列</span></span>
<span class="line"><span>       │    │    │   └────── 第3层</span></span>
<span class="line"><span>       │    │    └────────── 第1巷道</span></span>
<span class="line"><span>       │    └─────────────── A01库区</span></span>
<span class="line"><span>       └──────────────────── WH01仓库</span></span></code></pre>
</div><h3 id="数据删除顺序" tabindex="-1">数据删除顺序 <a class="header-anchor" href="#数据删除顺序" aria-label="Permalink to &quot;数据删除顺序&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>层级数据存在关联关系，删除时需从下往上：</p>
<p><strong>容器 → 储位 → 巷道 → 库区 → 仓库</strong></p>
</div>
<hr>
<h2 id="相关链接" tabindex="-1">相关链接 <a class="header-anchor" href="#相关链接" aria-label="Permalink to &quot;相关链接&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/business/workflow.html">工作流插件</a></li>
<li><a href="/zh/business/tms.html">运输管理（TMS）</a></li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="仓储管理插件-wms" tabindex="-1">仓储管理插件（WMS） <a class="header-anchor" href="#仓储管理插件-wms" aria-label="Permalink to &quot;仓储管理插件（WMS）&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">核心信息</p>
<ul>
<li><strong>全称</strong>：Warehouse Management System</li>
<li><strong>服务</strong>：<code>wemirr-platform-wms</code></li>
<li><strong>端口</strong>：<code>5006</code></li>
</ul>
</div>
<hr>
<h2 id="概念介绍" tabindex="-1">概念介绍 <a class="header-anchor" href="#概念介绍" aria-label="Permalink to &quot;概念介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="什么是-wms" tabindex="-1">什么是 WMS？ <a class="header-anchor" href="#什么是-wms" aria-label="Permalink to &quot;什么是 WMS？&quot;">&ZeroWidthSpace;</a></h3>
<p>WMS 是仓储管理系统，用于管理仓库内货物的存储、流转和作业全过程。</p>
<p><strong>核心价值</strong>：提升仓库空间利用率、优化作业效率、实现库存精准管理。</p>
<h3 id="仓库层级结构" tabindex="-1">仓库层级结构 <a class="header-anchor" href="#仓库层级结构" aria-label="Permalink to &quot;仓库层级结构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                      仓库 Warehouse                      │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│                                                         │</span></span>
<span class="line"><span>│   ┌─────────────────────────────────────────────────┐  │</span></span>
<span class="line"><span>│   │              库区 Storage Area                   │  │</span></span>
<span class="line"><span>│   │  ┌─────────────────────────────────────────┐    │  │</span></span>
<span class="line"><span>│   │  │           巷道 Aisle                     │    │  │</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>│                                                         │</span></span>
<span class="line"><span>│   ┌──────────┐                      ┌──────────┐       │</span></span>
<span class="line"><span>│   │  月台    │                      │  工作台   │       │</span></span>
<span class="line"><span>│   │  Dock    │                      │ Workbench │       │</span></span>
<span class="line"><span>│   └──────────┘                      └──────────┘       │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────┘</span></span></code></pre>
</div><h3 id="层级说明" tabindex="-1">层级说明 <a class="header-anchor" href="#层级说明" aria-label="Permalink to &quot;层级说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>层级</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>仓库</strong></td>
<td>顶层管理单元，代表物理仓库</td>
</tr>
<tr>
<td><strong>库区</strong></td>
<td>按功能划分（收货区、存储区、发货区）</td>
</tr>
<tr>
<td><strong>巷道</strong></td>
<td>库区内的通道</td>
</tr>
<tr>
<td><strong>储位</strong></td>
<td>存放货物的最小位置单元</td>
</tr>
<tr>
<td><strong>容器</strong></td>
<td>存放货物的载体（托盘、周转箱）</td>
</tr>
<tr>
<td><strong>月台</strong></td>
<td>装卸货平台</td>
</tr>
<tr>
<td><strong>工作台</strong></td>
<td>操作人员工作站点</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="功能模块" tabindex="-1">功能模块 <a class="header-anchor" href="#功能模块" aria-label="Permalink to &quot;功能模块&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础数据" tabindex="-1">基础数据 <a class="header-anchor" href="#基础数据" aria-label="Permalink to &quot;基础数据&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>仓库管理</td>
<td>仓库基本信息配置</td>
</tr>
<tr>
<td>库区管理</td>
<td>划分存储区域</td>
</tr>
<tr>
<td>储位管理</td>
<td>定义存储位置</td>
</tr>
<tr>
<td>容器管理</td>
<td>管理托盘、周转箱等</td>
</tr>
</tbody>
</table>
<h3 id="仓库作业" tabindex="-1">仓库作业 <a class="header-anchor" href="#仓库作业" aria-label="Permalink to &quot;仓库作业&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>入库管理</td>
<td>收货、质检、上架</td>
</tr>
<tr>
<td>出库管理</td>
<td>拣货、复核、发货</td>
</tr>
<tr>
<td>库内作业</td>
<td>移库、盘点、调整</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1️⃣-启动服务" tabindex="-1">1️⃣ 启动服务 <a class="header-anchor" href="#_1️⃣-启动服务" aria-label="Permalink to &quot;1️⃣ 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># IDEA 中运行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">WmsApplication.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或命令行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> spring-boot:run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -pl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-plugin/wemirr-platform-wms</span></span></code></pre>
</div><h3 id="_2️⃣-初始化仓库" tabindex="-1">2️⃣ 初始化仓库 <a class="header-anchor" href="#_2️⃣-初始化仓库" aria-label="Permalink to &quot;2️⃣ 初始化仓库&quot;">&ZeroWidthSpace;</a></h3>
<p>按层级依次创建：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>仓库 → 库区 → 巷道 → 储位 → 容器</span></span></code></pre>
</div><h3 id="_3️⃣-开始作业" tabindex="-1">3️⃣ 开始作业 <a class="header-anchor" href="#_3️⃣-开始作业" aria-label="Permalink to &quot;3️⃣ 开始作业&quot;">&ZeroWidthSpace;</a></h3>
<p>配置完成后即可进行入库、出库等仓储作业。</p>
<hr>
<h2 id="业务流程" tabindex="-1">业务流程 <a class="header-anchor" href="#业务流程" aria-label="Permalink to &quot;业务流程&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="入库流程" tabindex="-1">入库流程 <a class="header-anchor" href="#入库流程" aria-label="Permalink to &quot;入库流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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></code></pre>
</div><h3 id="出库流程" tabindex="-1">出库流程 <a class="header-anchor" href="#出库流程" aria-label="Permalink to &quot;出库流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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></code></pre>
</div><hr>
<h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="储位编码规范" tabindex="-1">储位编码规范 <a class="header-anchor" href="#储位编码规范" aria-label="Permalink to &quot;储位编码规范&quot;">&ZeroWidthSpace;</a></h3>
<p>建议采用层级编码，便于快速定位：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>格式：仓库-库区-巷道-层-列</span></span>
<span class="line"><span>示例：WH01-A01-01-03-05</span></span>
<span class="line"><span>       │    │    │   │   └── 第5列</span></span>
<span class="line"><span>       │    │    │   └────── 第3层</span></span>
<span class="line"><span>       │    │    └────────── 第1巷道</span></span>
<span class="line"><span>       │    └─────────────── A01库区</span></span>
<span class="line"><span>       └──────────────────── WH01仓库</span></span></code></pre>
</div><h3 id="数据删除顺序" tabindex="-1">数据删除顺序 <a class="header-anchor" href="#数据删除顺序" aria-label="Permalink to &quot;数据删除顺序&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>层级数据存在关联关系，删除时需从下往上：</p>
<p><strong>容器 → 储位 → 巷道 → 库区 → 仓库</strong></p>
</div>
<hr>
<h2 id="相关链接" tabindex="-1">相关链接 <a class="header-anchor" href="#相关链接" aria-label="Permalink to &quot;相关链接&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/business/workflow.html">工作流插件</a></li>
<li><a href="/zh/business/tms.html">运输管理（TMS）</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[工作流插件 ]]></title>
            <link>https://docs.battcn.com/zh/business/workflow.html</link>
            <guid>https://docs.battcn.com/zh/business/workflow.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="工作流插件" tabindex="-1">工作流插件 <a class="header-anchor" href="#工作流插件" aria-label="Permalink to &quot;工作流插件&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">核心信息</p>
<ul>
<li><strong>引擎</strong>：<a href="https://warm-flow.cn/" target="_blank" rel="noreferrer">Warm-Flow</a> —— 国产轻量级工作流引擎</li>
<li><strong>服务</strong>：<code>wemirr-platform-workflow</code></li>
<li><strong>端口</strong>：<code>5004</code></li>
</ul>
</div>
<hr>
<h2 id="概念介绍" tabindex="-1">概念介绍 <a class="header-anchor" href="#概念介绍" aria-label="Permalink to &quot;概念介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="什么是工作流" tabindex="-1">什么是工作流？ <a class="header-anchor" href="#什么是工作流" aria-label="Permalink to &quot;什么是工作流？&quot;">&ZeroWidthSpace;</a></h3>
<p>工作流是将业务流程中的各个环节按照规则串联起来，实现任务的自动流转和协同处理。</p>
<p><strong>典型场景</strong>：请假审批、报销审批、合同签订、采购审批等。</p>
<h3 id="核心概念" tabindex="-1">核心概念 <a class="header-anchor" href="#核心概念" aria-label="Permalink to &quot;核心概念&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐      ┌─────────────┐      ┌─────────────┐</span></span>
<span class="line"><span>│  流程定义    │  →   │  流程实例    │  →   │   任务      │</span></span>
<span class="line"><span>│ Definition  │      │  Instance   │      │   Task      │</span></span>
<span class="line"><span>└─────────────┘      └─────────────┘      └─────────────┘</span></span>
<span class="line"><span>     模板                运行时               待办项</span></span></code></pre>
</div><table tabindex="0">
<thead>
<tr>
<th>概念</th>
<th>说明</th>
<th>类比</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>流程定义</strong></td>
<td>流程的模板，定义节点和流转规则</td>
<td>请假单模板</td>
</tr>
<tr>
<td><strong>流程实例</strong></td>
<td>根据定义发起的具体流程</td>
<td>张三的请假单</td>
</tr>
<tr>
<td><strong>任务</strong></td>
<td>流程中需要人处理的节点</td>
<td>等待经理审批</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="架构设计" tabindex="-1">架构设计 <a class="header-anchor" href="#架构设计" aria-label="Permalink to &quot;架构设计&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│  └──────────┘  └──────────┘  └──────────┘  └──────────┘ │</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>│                   Workflow Service                       │</span></span>
<span class="line"><span>│  ┌──────────────────────────────────────────────────┐   │</span></span>
<span class="line"><span>│  │                  Warm-Flow 引擎                   │   │</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>│         MySQL（流程数据）  +  Redis（分布式锁）          │</span></span>
<span class="line"><span>└──────────────────────────────────────────────────────────┘</span></span></code></pre>
</div><hr>
<h2 id="功能模块" tabindex="-1">功能模块 <a class="header-anchor" href="#功能模块" aria-label="Permalink to &quot;功能模块&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="流程管理" tabindex="-1">流程管理 <a class="header-anchor" href="#流程管理" aria-label="Permalink to &quot;流程管理&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>流程分类</td>
<td>按业务类型分类（请假、报销、采购等）</td>
</tr>
<tr>
<td>流程设计</td>
<td>可视化设计器，拖拽配置节点</td>
</tr>
<tr>
<td>版本管理</td>
<td>支持流程定义的版本控制</td>
</tr>
</tbody>
</table>
<h3 id="任务中心" tabindex="-1">任务中心 <a class="header-anchor" href="#任务中心" aria-label="Permalink to &quot;任务中心&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>我的待办</td>
<td>当前需要处理的任务</td>
</tr>
<tr>
<td>我的已办</td>
<td>已处理完成的任务</td>
</tr>
<tr>
<td>我发起的</td>
<td>自己发起的流程</td>
</tr>
</tbody>
</table>
<h3 id="审批操作" tabindex="-1">审批操作 <a class="header-anchor" href="#审批操作" aria-label="Permalink to &quot;审批操作&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>操作</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>通过</strong></td>
<td>同意，流程流转到下一节点</td>
</tr>
<tr>
<td><strong>拒绝</strong></td>
<td>不同意，流程终止</td>
</tr>
<tr>
<td><strong>驳回</strong></td>
<td>退回到之前的节点重新处理</td>
</tr>
<tr>
<td><strong>转办</strong></td>
<td>将任务转交给其他人</td>
</tr>
<tr>
<td><strong>加签</strong></td>
<td>临时增加审批人</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1️⃣-启动服务" tabindex="-1">1️⃣ 启动服务 <a class="header-anchor" href="#_1️⃣-启动服务" aria-label="Permalink to &quot;1️⃣ 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># IDEA 中运行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">WorkFlowApplication.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或命令行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> spring-boot:run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -pl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-plugin/wemirr-platform-workflow</span></span></code></pre>
</div><h3 id="_2️⃣-设计流程" tabindex="-1">2️⃣ 设计流程 <a class="header-anchor" href="#_2️⃣-设计流程" aria-label="Permalink to &quot;2️⃣ 设计流程&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【工作流】→【流程定义】→【新建】，使用可视化设计器配置流程。</p>
<h3 id="_3️⃣-发起流程" tabindex="-1">3️⃣ 发起流程 <a class="header-anchor" href="#_3️⃣-发起流程" aria-label="Permalink to &quot;3️⃣ 发起流程&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【发起流程】，选择流程模板，填写表单后提交。</p>
<h3 id="_4️⃣-处理待办" tabindex="-1">4️⃣ 处理待办 <a class="header-anchor" href="#_4️⃣-处理待办" aria-label="Permalink to &quot;4️⃣ 处理待办&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【我的待办】，查看并处理待审批的任务。</p>
<hr>
<h2 id="api-接口-真实代码" tabindex="-1">API 接口（真实代码） <a class="header-anchor" href="#api-接口-真实代码" aria-label="Permalink to &quot;API 接口（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>以下接口来自 <code>wemirr-platform-workflow</code> 模块的真实实现。</p>
<h3 id="流程任务-api" tabindex="-1">流程任务 API <a class="header-anchor" href="#流程任务-api" aria-label="Permalink to &quot;流程任务 API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 FlowTaskController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/flow-tasks"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程任务"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程任务管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FlowTaskController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/todo/mine"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "我的待办"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TodoTaskPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">myTodo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TaskPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/done/mine"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "我的已办"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DoneTaskPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">myDone</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TaskPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/approve"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "workflow:task:handle"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "请稍后重试"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "审批通过"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> approve</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/reject"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "审批拒绝"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> reject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/return"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务驳回"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> returnTask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/terminate"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务终止"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> terminate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/transfer"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务转办"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> transfer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/sign/add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务加签"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> addSign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/sign/remove"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务减签"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> removeSign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="流程实例-api" tabindex="-1">流程实例 API <a class="header-anchor" href="#流程实例-api" aria-label="Permalink to &quot;流程实例 API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 FlowInstanceController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/flow-instances"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程实例"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程实例管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FlowInstanceController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "分页查询"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">InstancePageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(InstancePageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/detail"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "实例详情"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> InstanceExtDetailResp </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">detail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/mine"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "我的流程"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">InstancePageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mine</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(InstancePageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/active"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "激活实例"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> active</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/suspend"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "挂起实例"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> suspend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/tasks"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Task</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tasks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/tasks/history"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "历史任务"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FlowTaskApproveListResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">taskHistory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/form"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "表单预览"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ProcessInstanceFormPreviewResp </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="任务处理请求" tabindex="-1">任务处理请求 <a class="header-anchor" href="#任务处理请求" aria-label="Permalink to &quot;任务处理请求&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 WorkflowTaskReq.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> WorkflowTaskReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务处理意见不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务处理意见"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "处理对象"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">example</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "1,role:1,role:2"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String handlerObject;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程变量"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> variable </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> HashMap&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="service-实现" tabindex="-1">Service 实现 <a class="header-anchor" href="#service-实现" aria-label="Permalink to &quot;Service 实现&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="审批通过" tabindex="-1">审批通过 <a class="header-anchor" href="#审批通过" aria-label="Permalink to &quot;审批通过&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TaskExtServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> variable </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ObjUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">defaultIfNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), Maps.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newHashMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    variable.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(VariableConstant.VAR_APPROVE_USER, context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nickName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    FlowParams flowParams </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">skipType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ApprovalType.PASS.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hisTaskExt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ApprovalAction.PASS.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">variable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(variable);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">skip</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, flowParams);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="任务驳回" tabindex="-1">任务驳回 <a class="header-anchor" href="#任务驳回" aria-label="Permalink to &quot;任务驳回&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> taskReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    FlowParams flowParams </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    flowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rejectLast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, flowParams);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 驳回到上一节点</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="转办-加签-减签" tabindex="-1">转办/加签/减签 <a class="header-anchor" href="#转办-加签-减签" aria-label="Permalink to &quot;转办/加签/减签&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 转办</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> transfer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long taskId, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">transfer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(taskId, FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addHandlers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 加签</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> addSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long taskId, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(taskId, FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addHandlers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 减签</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> removeSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long taskId, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reductionSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(taskId, FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reductionHandlers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// handlerObject 格式：用户ID 或 role:角色ID</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHandlerObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"需要修改的对象不能为空！"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">split</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHandlerObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">","</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="分布式锁保护" tabindex="-1">分布式锁保护 <a class="header-anchor" href="#分布式锁保护" aria-label="Permalink to &quot;分布式锁保护&quot;">&ZeroWidthSpace;</a></h2>
<p>审批操作使用 <code>@RedisLock</code> 防止并发处理：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/approve"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "workflow:task:handle"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "当前已有任务处理中，请稍后重试"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> approve</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskExtService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="扩展开发" tabindex="-1">扩展开发 <a class="header-anchor" href="#扩展开发" aria-label="Permalink to &quot;扩展开发&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="自定义表单" tabindex="-1">自定义表单 <a class="header-anchor" href="#自定义表单" aria-label="Permalink to &quot;自定义表单&quot;">&ZeroWidthSpace;</a></h3>
<p>业务表单需在前端注册映射关系：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// apps/web-antd/src/views/wemirr/workflow/register.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> flowComponentsMap</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  '/workflow/leave/index'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: LeaveForm,        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 请假表单</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  '/workflow/expense/index'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ExpenseForm,    </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 报销表单</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="监听器扩展" tabindex="-1">监听器扩展 <a class="header-anchor" href="#监听器扩展" aria-label="Permalink to &quot;监听器扩展&quot;">&ZeroWidthSpace;</a></h3>
<p>可通过 Warm-Flow 的监听器在流程节点执行前后插入业务逻辑。</p>
<h3 id="处理人格式" tabindex="-1">处理人格式 <a class="header-anchor" href="#处理人格式" aria-label="Permalink to &quot;处理人格式&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>格式</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>{userId}</code></td>
<td>指定用户</td>
<td><code>1001</code></td>
</tr>
<tr>
<td><code>role:{roleId}</code></td>
<td>指定角色</td>
<td><code>role:1</code></td>
</tr>
<tr>
<td>多人</td>
<td>逗号分隔</td>
<td><code>1001,1002,role:1</code></td>
</tr>
</tbody>
</table>
<hr>
<h2 id="相关链接" tabindex="-1">相关链接 <a class="header-anchor" href="#相关链接" aria-label="Permalink to &quot;相关链接&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/business/tms.html">运输管理（TMS）</a></li>
<li><a href="/zh/business/wms.html">仓储管理（WMS）</a></li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="工作流插件" tabindex="-1">工作流插件 <a class="header-anchor" href="#工作流插件" aria-label="Permalink to &quot;工作流插件&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">核心信息</p>
<ul>
<li><strong>引擎</strong>：<a href="https://warm-flow.cn/" target="_blank" rel="noreferrer">Warm-Flow</a> —— 国产轻量级工作流引擎</li>
<li><strong>服务</strong>：<code>wemirr-platform-workflow</code></li>
<li><strong>端口</strong>：<code>5004</code></li>
</ul>
</div>
<hr>
<h2 id="概念介绍" tabindex="-1">概念介绍 <a class="header-anchor" href="#概念介绍" aria-label="Permalink to &quot;概念介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="什么是工作流" tabindex="-1">什么是工作流？ <a class="header-anchor" href="#什么是工作流" aria-label="Permalink to &quot;什么是工作流？&quot;">&ZeroWidthSpace;</a></h3>
<p>工作流是将业务流程中的各个环节按照规则串联起来，实现任务的自动流转和协同处理。</p>
<p><strong>典型场景</strong>：请假审批、报销审批、合同签订、采购审批等。</p>
<h3 id="核心概念" tabindex="-1">核心概念 <a class="header-anchor" href="#核心概念" aria-label="Permalink to &quot;核心概念&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐      ┌─────────────┐      ┌─────────────┐</span></span>
<span class="line"><span>│  流程定义    │  →   │  流程实例    │  →   │   任务      │</span></span>
<span class="line"><span>│ Definition  │      │  Instance   │      │   Task      │</span></span>
<span class="line"><span>└─────────────┘      └─────────────┘      └─────────────┘</span></span>
<span class="line"><span>     模板                运行时               待办项</span></span></code></pre>
</div><table tabindex="0">
<thead>
<tr>
<th>概念</th>
<th>说明</th>
<th>类比</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>流程定义</strong></td>
<td>流程的模板，定义节点和流转规则</td>
<td>请假单模板</td>
</tr>
<tr>
<td><strong>流程实例</strong></td>
<td>根据定义发起的具体流程</td>
<td>张三的请假单</td>
</tr>
<tr>
<td><strong>任务</strong></td>
<td>流程中需要人处理的节点</td>
<td>等待经理审批</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="架构设计" tabindex="-1">架构设计 <a class="header-anchor" href="#架构设计" aria-label="Permalink to &quot;架构设计&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│  └──────────┘  └──────────┘  └──────────┘  └──────────┘ │</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>│                   Workflow Service                       │</span></span>
<span class="line"><span>│  ┌──────────────────────────────────────────────────┐   │</span></span>
<span class="line"><span>│  │                  Warm-Flow 引擎                   │   │</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>│         MySQL（流程数据）  +  Redis（分布式锁）          │</span></span>
<span class="line"><span>└──────────────────────────────────────────────────────────┘</span></span></code></pre>
</div><hr>
<h2 id="功能模块" tabindex="-1">功能模块 <a class="header-anchor" href="#功能模块" aria-label="Permalink to &quot;功能模块&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="流程管理" tabindex="-1">流程管理 <a class="header-anchor" href="#流程管理" aria-label="Permalink to &quot;流程管理&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>流程分类</td>
<td>按业务类型分类（请假、报销、采购等）</td>
</tr>
<tr>
<td>流程设计</td>
<td>可视化设计器，拖拽配置节点</td>
</tr>
<tr>
<td>版本管理</td>
<td>支持流程定义的版本控制</td>
</tr>
</tbody>
</table>
<h3 id="任务中心" tabindex="-1">任务中心 <a class="header-anchor" href="#任务中心" aria-label="Permalink to &quot;任务中心&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>我的待办</td>
<td>当前需要处理的任务</td>
</tr>
<tr>
<td>我的已办</td>
<td>已处理完成的任务</td>
</tr>
<tr>
<td>我发起的</td>
<td>自己发起的流程</td>
</tr>
</tbody>
</table>
<h3 id="审批操作" tabindex="-1">审批操作 <a class="header-anchor" href="#审批操作" aria-label="Permalink to &quot;审批操作&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>操作</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>通过</strong></td>
<td>同意，流程流转到下一节点</td>
</tr>
<tr>
<td><strong>拒绝</strong></td>
<td>不同意，流程终止</td>
</tr>
<tr>
<td><strong>驳回</strong></td>
<td>退回到之前的节点重新处理</td>
</tr>
<tr>
<td><strong>转办</strong></td>
<td>将任务转交给其他人</td>
</tr>
<tr>
<td><strong>加签</strong></td>
<td>临时增加审批人</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1️⃣-启动服务" tabindex="-1">1️⃣ 启动服务 <a class="header-anchor" href="#_1️⃣-启动服务" aria-label="Permalink to &quot;1️⃣ 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># IDEA 中运行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">WorkFlowApplication.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或命令行</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> spring-boot:run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -pl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-plugin/wemirr-platform-workflow</span></span></code></pre>
</div><h3 id="_2️⃣-设计流程" tabindex="-1">2️⃣ 设计流程 <a class="header-anchor" href="#_2️⃣-设计流程" aria-label="Permalink to &quot;2️⃣ 设计流程&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【工作流】→【流程定义】→【新建】，使用可视化设计器配置流程。</p>
<h3 id="_3️⃣-发起流程" tabindex="-1">3️⃣ 发起流程 <a class="header-anchor" href="#_3️⃣-发起流程" aria-label="Permalink to &quot;3️⃣ 发起流程&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【发起流程】，选择流程模板，填写表单后提交。</p>
<h3 id="_4️⃣-处理待办" tabindex="-1">4️⃣ 处理待办 <a class="header-anchor" href="#_4️⃣-处理待办" aria-label="Permalink to &quot;4️⃣ 处理待办&quot;">&ZeroWidthSpace;</a></h3>
<p>进入【我的待办】，查看并处理待审批的任务。</p>
<hr>
<h2 id="api-接口-真实代码" tabindex="-1">API 接口（真实代码） <a class="header-anchor" href="#api-接口-真实代码" aria-label="Permalink to &quot;API 接口（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>以下接口来自 <code>wemirr-platform-workflow</code> 模块的真实实现。</p>
<h3 id="流程任务-api" tabindex="-1">流程任务 API <a class="header-anchor" href="#流程任务-api" aria-label="Permalink to &quot;流程任务 API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 FlowTaskController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/flow-tasks"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程任务"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程任务管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FlowTaskController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/todo/mine"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "我的待办"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TodoTaskPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">myTodo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TaskPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/done/mine"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "我的已办"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DoneTaskPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">myDone</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TaskPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/approve"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "workflow:task:handle"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "请稍后重试"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "审批通过"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> approve</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/reject"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "审批拒绝"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> reject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/return"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务驳回"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> returnTask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/terminate"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务终止"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> terminate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/transfer"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务转办"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> transfer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/sign/add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务加签"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> addSign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/sign/remove"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务减签"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> removeSign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="流程实例-api" tabindex="-1">流程实例 API <a class="header-anchor" href="#流程实例-api" aria-label="Permalink to &quot;流程实例 API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 FlowInstanceController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/flow-instances"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程实例"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程实例管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FlowInstanceController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "分页查询"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">InstancePageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(InstancePageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/detail"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "实例详情"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> InstanceExtDetailResp </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">detail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/mine"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "我的流程"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">InstancePageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mine</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(InstancePageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/active"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "激活实例"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> active</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/suspend"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "挂起实例"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> suspend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/tasks"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Task</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tasks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/tasks/history"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "历史任务"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FlowTaskApproveListResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">taskHistory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/form"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "表单预览"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ProcessInstanceFormPreviewResp </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="任务处理请求" tabindex="-1">任务处理请求 <a class="header-anchor" href="#任务处理请求" aria-label="Permalink to &quot;任务处理请求&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 WorkflowTaskReq.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> WorkflowTaskReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务处理意见不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "任务处理意见"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "处理对象"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">example</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "1,role:1,role:2"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String handlerObject;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "流程变量"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> variable </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> HashMap&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="service-实现" tabindex="-1">Service 实现 <a class="header-anchor" href="#service-实现" aria-label="Permalink to &quot;Service 实现&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="审批通过" tabindex="-1">审批通过 <a class="header-anchor" href="#审批通过" aria-label="Permalink to &quot;审批通过&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TaskExtServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> variable </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ObjUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">defaultIfNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), Maps.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newHashMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    variable.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(VariableConstant.VAR_APPROVE_USER, context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nickName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    FlowParams flowParams </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">skipType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ApprovalType.PASS.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hisTaskExt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ApprovalAction.PASS.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">variable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(variable);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">skip</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, flowParams);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="任务驳回" tabindex="-1">任务驳回 <a class="header-anchor" href="#任务驳回" aria-label="Permalink to &quot;任务驳回&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> taskReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    FlowParams flowParams </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    flowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rejectLast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, flowParams);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 驳回到上一节点</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="转办-加签-减签" tabindex="-1">转办/加签/减签 <a class="header-anchor" href="#转办-加签-减签" aria-label="Permalink to &quot;转办/加签/减签&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 转办</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> transfer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long taskId, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">transfer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(taskId, FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addHandlers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 加签</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> addSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long taskId, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(taskId, FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addHandlers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 减签</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> removeSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long taskId, WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reductionSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(taskId, FlowParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reductionHandlers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// handlerObject 格式：用户ID 或 role:角色ID</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handlerChangeObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHandlerObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"需要修改的对象不能为空！"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">split</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHandlerObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">","</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="分布式锁保护" tabindex="-1">分布式锁保护 <a class="header-anchor" href="#分布式锁保护" aria-label="Permalink to &quot;分布式锁保护&quot;">&ZeroWidthSpace;</a></h2>
<p>审批操作使用 <code>@RedisLock</code> 防止并发处理：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/approve"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "workflow:task:handle"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "当前已有任务处理中，请稍后重试"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> approve</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WorkflowTaskReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    taskExtService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="扩展开发" tabindex="-1">扩展开发 <a class="header-anchor" href="#扩展开发" aria-label="Permalink to &quot;扩展开发&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="自定义表单" tabindex="-1">自定义表单 <a class="header-anchor" href="#自定义表单" aria-label="Permalink to &quot;自定义表单&quot;">&ZeroWidthSpace;</a></h3>
<p>业务表单需在前端注册映射关系：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// apps/web-antd/src/views/wemirr/workflow/register.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> flowComponentsMap</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  '/workflow/leave/index'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: LeaveForm,        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 请假表单</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  '/workflow/expense/index'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ExpenseForm,    </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 报销表单</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="监听器扩展" tabindex="-1">监听器扩展 <a class="header-anchor" href="#监听器扩展" aria-label="Permalink to &quot;监听器扩展&quot;">&ZeroWidthSpace;</a></h3>
<p>可通过 Warm-Flow 的监听器在流程节点执行前后插入业务逻辑。</p>
<h3 id="处理人格式" tabindex="-1">处理人格式 <a class="header-anchor" href="#处理人格式" aria-label="Permalink to &quot;处理人格式&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>格式</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>{userId}</code></td>
<td>指定用户</td>
<td><code>1001</code></td>
</tr>
<tr>
<td><code>role:{roleId}</code></td>
<td>指定角色</td>
<td><code>role:1</code></td>
</tr>
<tr>
<td>多人</td>
<td>逗号分隔</td>
<td><code>1001,1002,role:1</code></td>
</tr>
</tbody>
</table>
<hr>
<h2 id="相关链接" tabindex="-1">相关链接 <a class="header-anchor" href="#相关链接" aria-label="Permalink to &quot;相关链接&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/business/tms.html">运输管理（TMS）</a></li>
<li><a href="/zh/business/wms.html">仓储管理（WMS）</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[常见问题 ]]></title>
            <link>https://docs.battcn.com/zh/faq.html</link>
            <guid>https://docs.battcn.com/zh/faq.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">&ZeroWidthSpace;</a></h1>
<nav class="table-of-contents"><ul><li><a href="#启动入口">启动入口</a></li><li><a href="#服务连接">服务连接</a></li></ul></nav>
<h2 id="启动入口" tabindex="-1">启动入口 <a class="header-anchor" href="#启动入口" aria-label="Permalink to &quot;启动入口&quot;">&ZeroWidthSpace;</a></h2>
<p>正常情况下，WEMIRR-PLATFORM 在启动时，会在控制台打印如下 Banner 信息，包含版本与官方网址，如果在项目启动中没有发现 WEMIRR-PLATFORM 的 Banner 打印，那就说明 WEMIRR-PLATFORM 没有被正常加载。</p>
<div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>/**</span></span>
<span class="line"><span> *                             _ooOoo_</span></span>
<span class="line"><span> *                            o8888888o</span></span>
<span class="line"><span> *                            88" . "88</span></span>
<span class="line"><span> *                            (| -_- |)</span></span>
<span class="line"><span> *                            O\  =  /O</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></span>
<span class="line"><span> *                     | \_|  ''\---/''  |   |</span></span>
<span class="line"><span> *                     \  .-\__  `-`  ___/-. /</span></span>
<span class="line"><span> *                   ___`. .'  /--.--\  `. . __</span></span>
<span class="line"><span> *                ."" '&#x3C;  `.___\_&#x3C;|>_/___.'  >'"".</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> *                     佛祖保佑        永无BUG</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> *                   别人笑我忒疯癫，我笑自己命太贱；</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;">&ZeroWidthSpace;</a></h2>
]]></description>
            <content:encoded><![CDATA[<h1 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">&ZeroWidthSpace;</a></h1>
<nav class="table-of-contents"><ul><li><a href="#启动入口">启动入口</a></li><li><a href="#服务连接">服务连接</a></li></ul></nav>
<h2 id="启动入口" tabindex="-1">启动入口 <a class="header-anchor" href="#启动入口" aria-label="Permalink to &quot;启动入口&quot;">&ZeroWidthSpace;</a></h2>
<p>正常情况下，WEMIRR-PLATFORM 在启动时，会在控制台打印如下 Banner 信息，包含版本与官方网址，如果在项目启动中没有发现 WEMIRR-PLATFORM 的 Banner 打印，那就说明 WEMIRR-PLATFORM 没有被正常加载。</p>
<div class="language-txt vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">txt</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>/**</span></span>
<span class="line"><span> *                             _ooOoo_</span></span>
<span class="line"><span> *                            o8888888o</span></span>
<span class="line"><span> *                            88" . "88</span></span>
<span class="line"><span> *                            (| -_- |)</span></span>
<span class="line"><span> *                            O\  =  /O</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></span>
<span class="line"><span> *                     | \_|  ''\---/''  |   |</span></span>
<span class="line"><span> *                     \  .-\__  `-`  ___/-. /</span></span>
<span class="line"><span> *                   ___`. .'  /--.--\  `. . __</span></span>
<span class="line"><span> *                ."" '&#x3C;  `.___\_&#x3C;|>_/___.'  >'"".</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> *                     佛祖保佑        永无BUG</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> *                   别人笑我忒疯癫，我笑自己命太贱；</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;">&ZeroWidthSpace;</a></h2>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[常见问题 ]]></title>
            <link>https://docs.battcn.com/zh/faq/</link>
            <guid>https://docs.battcn.com/zh/faq/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">&ZeroWidthSpace;</a></h1>
<hr>
<h2 id="环境配置" tabindex="-1">环境配置 <a class="header-anchor" href="#环境配置" aria-label="Permalink to &quot;环境配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="nacos-连接失败" tabindex="-1">Nacos 连接失败 <a class="header-anchor" href="#nacos-连接失败" aria-label="Permalink to &quot;Nacos 连接失败&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>Connection refused</code> 或 <code>NacosException</code></p>
<p><strong>解决</strong>：</p>
<ul>
<li>确保 Nacos 已启动，访问 <code>http://localhost:8848/nacos</code> 能打开控制台</li>
<li>检查防火墙，需开放 <code>8848</code>（HTTP）和 <code>9848</code>（gRPC）端口</li>
<li>使用 Nginx 代理时必须用 TCP 转发，不能用 HTTP2</li>
</ul>
<h3 id="无数据源异常" tabindex="-1">无数据源异常 <a class="header-anchor" href="#无数据源异常" aria-label="Permalink to &quot;无数据源异常&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：启动报 <code>No DataSource found</code></p>
<p><strong>解决</strong>：</p>
<ol>
<li>登录 Nacos 控制台</li>
<li>创建命名空间 <code>v4-dev</code>（或修改 <code>application.yml</code> 的 namespace）</li>
<li>导入项目 <code>附件/nacos/</code> 下的配置 ZIP 包</li>
</ol>
<h3 id="minio-文件上传报错" tabindex="-1">MINIO 文件上传报错 <a class="header-anchor" href="#minio-文件上传报错" aria-label="Permalink to &quot;MINIO 文件上传报错&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>XmlParserException: 前言中不允许有内容</code></p>
<p><strong>解决</strong>：检查 MINIO 端口配置，API 端口默认 <code>9000</code>，控制台端口 <code>9001</code>，不要混淆</p>
<h3 id="redis-连接失败" tabindex="-1">Redis 连接失败 <a class="header-anchor" href="#redis-连接失败" aria-label="Permalink to &quot;Redis 连接失败&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>Cannot get Jedis connection</code></p>
<p><strong>解决</strong>：</p>
<ul>
<li>检查 Redis 服务是否启动</li>
<li>检查 Nacos 中的 <code>redis.properties</code> 配置</li>
<li>确认 Redis 密码是否正确</li>
</ul>
<hr>
<h2 id="后端开发" tabindex="-1">后端开发 <a class="header-anchor" href="#后端开发" aria-label="Permalink to &quot;后端开发&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="如何获取当前登录用户信息" tabindex="-1">如何获取当前登录用户信息 <a class="header-anchor" href="#如何获取当前登录用户信息" aria-label="Permalink to &quot;如何获取当前登录用户信息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DemoService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> demo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="接口-401-未授权" tabindex="-1">接口 401 未授权 <a class="header-anchor" href="#接口-401-未授权" aria-label="Permalink to &quot;接口 401 未授权&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查步骤</strong>：</p>
<ol>
<li>Token 是否过期 → 重新登录</li>
<li>接口是否需要权限 → 检查 <code>@PreAuthorize</code> 或菜单权限配置</li>
<li>网关是否正确转发 → 检查网关路由配置</li>
</ol>
<h3 id="新增服务如何注册到网关" tabindex="-1">新增服务如何注册到网关 <a class="header-anchor" href="#新增服务如何注册到网关" aria-label="Permalink to &quot;新增服务如何注册到网关&quot;">&ZeroWidthSpace;</a></h3>
<ol>
<li>服务添加 <code>spring-cloud-starter-alibaba-nacos-discovery</code> 依赖</li>
<li>配置 <code>spring.cloud.nacos.discovery.namespace</code> 与其他服务一致</li>
<li>网关 <code>routes</code> 中添加路由规则</li>
</ol>
<h3 id="mybatis-plus-分页不生效" tabindex="-1">MyBatis-Plus 分页不生效 <a class="header-anchor" href="#mybatis-plus-分页不生效" aria-label="Permalink to &quot;MyBatis-Plus 分页不生效&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>解决</strong>：确保配置了分页插件</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MybatisPlusInterceptor </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mybatisPlusInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    MybatisPlusInterceptor interceptor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MybatisPlusInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    interceptor.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PaginationInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DbType.MYSQL));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> interceptor;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="动态数据源切换" tabindex="-1">动态数据源切换 <a class="header-anchor" href="#动态数据源切换" aria-label="Permalink to &quot;动态数据源切换&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"slave"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> queryFromSlave</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="前端开发" tabindex="-1">前端开发 <a class="header-anchor" href="#前端开发" aria-label="Permalink to &quot;前端开发&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="前端启动报依赖错误" tabindex="-1">前端启动报依赖错误 <a class="header-anchor" href="#前端启动报依赖错误" aria-label="Permalink to &quot;前端启动报依赖错误&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>pnpm install</code> 或 <code>pnpm dev</code> 报错</p>
<p><strong>解决</strong>：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 确保 Node >= 20，pnpm >= 9</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">node</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 清理后重装</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> node_modules</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm-lock.yaml</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span></code></pre>
</div><h3 id="登录后一直跳转登录页" tabindex="-1">登录后一直跳转登录页 <a class="header-anchor" href="#登录后一直跳转登录页" aria-label="Permalink to &quot;登录后一直跳转登录页&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>解决</strong>：</p>
<ul>
<li>检查后端 IAM 服务是否启动</li>
<li>检查 Redis 是否正常运行</li>
<li>清除浏览器缓存和 LocalStorage</li>
</ul>
<h3 id="页面菜单不显示" tabindex="-1">页面菜单不显示 <a class="header-anchor" href="#页面菜单不显示" aria-label="Permalink to &quot;页面菜单不显示&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查</strong>：</p>
<ol>
<li>检查用户是否分配了角色</li>
<li>检查角色是否分配了菜单权限</li>
<li>清除浏览器缓存后重新登录</li>
</ol>
<h3 id="接口请求-404" tabindex="-1">接口请求 404 <a class="header-anchor" href="#接口请求-404" aria-label="Permalink to &quot;接口请求 404&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查</strong>：</p>
<ol>
<li>检查 <code>.env</code> 中的 <code>VITE_GLOB_API_URL</code> 配置</li>
<li>检查网关是否正常运行</li>
<li>检查接口路径是否正确</li>
</ol>
<hr>
<h2 id="数据库" tabindex="-1">数据库 <a class="header-anchor" href="#数据库" aria-label="Permalink to &quot;数据库&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="数据库连接失败" tabindex="-1">数据库连接失败 <a class="header-anchor" href="#数据库连接失败" aria-label="Permalink to &quot;数据库连接失败&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>Communications link failure</code> 或 <code>Access denied</code></p>
<p><strong>解决</strong>：</p>
<ul>
<li>检查 MySQL 服务是否启动</li>
<li>检查 Nacos 中的数据库配置（用户名、密码、端口）</li>
<li>确认 MySQL 允许远程连接</li>
</ul>
<h3 id="慢-sql-排查" tabindex="-1">慢 SQL 排查 <a class="header-anchor" href="#慢-sql-排查" aria-label="Permalink to &quot;慢 SQL 排查&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 开启慢查询日志</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> slow_query_log </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ON'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> long_query_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><hr>
<h2 id="多租户" tabindex="-1">多租户 <a class="header-anchor" href="#多租户" aria-label="Permalink to &quot;多租户&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="租户数据看不到" tabindex="-1">租户数据看不到 <a class="header-anchor" href="#租户数据看不到" aria-label="Permalink to &quot;租户数据看不到&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：切换租户后查询不到数据</p>
<p><strong>解决</strong>：</p>
<ul>
<li>确认数据的 <code>tenant_id</code> 字段值正确</li>
<li>检查当前登录用户是否属于该租户</li>
<li>超级管理员账号可能不受租户限制</li>
</ul>
<h3 id="多租户数据隔离失效" tabindex="-1">多租户数据隔离失效 <a class="header-anchor" href="#多租户数据隔离失效" aria-label="Permalink to &quot;多租户数据隔离失效&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查</strong>：</p>
<ul>
<li>检查表是否有 <code>tenant_id</code> 字段</li>
<li>检查实体类是否继承 <code>TenantEntity</code></li>
<li>检查是否使用 <code>@IgnoreTenant</code> 注解跳过了租户过滤</li>
</ul>
<h3 id="某些表不需要租户隔离" tabindex="-1">某些表不需要租户隔离 <a class="header-anchor" href="#某些表不需要租户隔离" aria-label="Permalink to &quot;某些表不需要租户隔离&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 <code>@IgnoreTenant</code> 注解：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">IgnoreTenant</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Dict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> queryAllDict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="获取帮助" tabindex="-1">获取帮助 <a class="header-anchor" href="#获取帮助" aria-label="Permalink to &quot;获取帮助&quot;">&ZeroWidthSpace;</a></h2>
<p>如果以上文档无法解决您的问题：</p>
<ul>
<li>💬 <a href="https://gitee.com/battcn/wemirr-platform/issues" target="_blank" rel="noreferrer">问题反馈</a></li>
<li>📧 <a href="https://gitee.com/battcn/" target="_blank" rel="noreferrer">联系我们</a></li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">&ZeroWidthSpace;</a></h1>
<hr>
<h2 id="环境配置" tabindex="-1">环境配置 <a class="header-anchor" href="#环境配置" aria-label="Permalink to &quot;环境配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="nacos-连接失败" tabindex="-1">Nacos 连接失败 <a class="header-anchor" href="#nacos-连接失败" aria-label="Permalink to &quot;Nacos 连接失败&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>Connection refused</code> 或 <code>NacosException</code></p>
<p><strong>解决</strong>：</p>
<ul>
<li>确保 Nacos 已启动，访问 <code>http://localhost:8848/nacos</code> 能打开控制台</li>
<li>检查防火墙，需开放 <code>8848</code>（HTTP）和 <code>9848</code>（gRPC）端口</li>
<li>使用 Nginx 代理时必须用 TCP 转发，不能用 HTTP2</li>
</ul>
<h3 id="无数据源异常" tabindex="-1">无数据源异常 <a class="header-anchor" href="#无数据源异常" aria-label="Permalink to &quot;无数据源异常&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：启动报 <code>No DataSource found</code></p>
<p><strong>解决</strong>：</p>
<ol>
<li>登录 Nacos 控制台</li>
<li>创建命名空间 <code>v4-dev</code>（或修改 <code>application.yml</code> 的 namespace）</li>
<li>导入项目 <code>附件/nacos/</code> 下的配置 ZIP 包</li>
</ol>
<h3 id="minio-文件上传报错" tabindex="-1">MINIO 文件上传报错 <a class="header-anchor" href="#minio-文件上传报错" aria-label="Permalink to &quot;MINIO 文件上传报错&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>XmlParserException: 前言中不允许有内容</code></p>
<p><strong>解决</strong>：检查 MINIO 端口配置，API 端口默认 <code>9000</code>，控制台端口 <code>9001</code>，不要混淆</p>
<h3 id="redis-连接失败" tabindex="-1">Redis 连接失败 <a class="header-anchor" href="#redis-连接失败" aria-label="Permalink to &quot;Redis 连接失败&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>Cannot get Jedis connection</code></p>
<p><strong>解决</strong>：</p>
<ul>
<li>检查 Redis 服务是否启动</li>
<li>检查 Nacos 中的 <code>redis.properties</code> 配置</li>
<li>确认 Redis 密码是否正确</li>
</ul>
<hr>
<h2 id="后端开发" tabindex="-1">后端开发 <a class="header-anchor" href="#后端开发" aria-label="Permalink to &quot;后端开发&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="如何获取当前登录用户信息" tabindex="-1">如何获取当前登录用户信息 <a class="header-anchor" href="#如何获取当前登录用户信息" aria-label="Permalink to &quot;如何获取当前登录用户信息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DemoService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> demo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="接口-401-未授权" tabindex="-1">接口 401 未授权 <a class="header-anchor" href="#接口-401-未授权" aria-label="Permalink to &quot;接口 401 未授权&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查步骤</strong>：</p>
<ol>
<li>Token 是否过期 → 重新登录</li>
<li>接口是否需要权限 → 检查 <code>@PreAuthorize</code> 或菜单权限配置</li>
<li>网关是否正确转发 → 检查网关路由配置</li>
</ol>
<h3 id="新增服务如何注册到网关" tabindex="-1">新增服务如何注册到网关 <a class="header-anchor" href="#新增服务如何注册到网关" aria-label="Permalink to &quot;新增服务如何注册到网关&quot;">&ZeroWidthSpace;</a></h3>
<ol>
<li>服务添加 <code>spring-cloud-starter-alibaba-nacos-discovery</code> 依赖</li>
<li>配置 <code>spring.cloud.nacos.discovery.namespace</code> 与其他服务一致</li>
<li>网关 <code>routes</code> 中添加路由规则</li>
</ol>
<h3 id="mybatis-plus-分页不生效" tabindex="-1">MyBatis-Plus 分页不生效 <a class="header-anchor" href="#mybatis-plus-分页不生效" aria-label="Permalink to &quot;MyBatis-Plus 分页不生效&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>解决</strong>：确保配置了分页插件</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MybatisPlusInterceptor </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mybatisPlusInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    MybatisPlusInterceptor interceptor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MybatisPlusInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    interceptor.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PaginationInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DbType.MYSQL));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> interceptor;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="动态数据源切换" tabindex="-1">动态数据源切换 <a class="header-anchor" href="#动态数据源切换" aria-label="Permalink to &quot;动态数据源切换&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"slave"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> queryFromSlave</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="前端开发" tabindex="-1">前端开发 <a class="header-anchor" href="#前端开发" aria-label="Permalink to &quot;前端开发&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="前端启动报依赖错误" tabindex="-1">前端启动报依赖错误 <a class="header-anchor" href="#前端启动报依赖错误" aria-label="Permalink to &quot;前端启动报依赖错误&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>pnpm install</code> 或 <code>pnpm dev</code> 报错</p>
<p><strong>解决</strong>：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 确保 Node >= 20，pnpm >= 9</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">node</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 清理后重装</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> node_modules</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm-lock.yaml</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span></code></pre>
</div><h3 id="登录后一直跳转登录页" tabindex="-1">登录后一直跳转登录页 <a class="header-anchor" href="#登录后一直跳转登录页" aria-label="Permalink to &quot;登录后一直跳转登录页&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>解决</strong>：</p>
<ul>
<li>检查后端 IAM 服务是否启动</li>
<li>检查 Redis 是否正常运行</li>
<li>清除浏览器缓存和 LocalStorage</li>
</ul>
<h3 id="页面菜单不显示" tabindex="-1">页面菜单不显示 <a class="header-anchor" href="#页面菜单不显示" aria-label="Permalink to &quot;页面菜单不显示&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查</strong>：</p>
<ol>
<li>检查用户是否分配了角色</li>
<li>检查角色是否分配了菜单权限</li>
<li>清除浏览器缓存后重新登录</li>
</ol>
<h3 id="接口请求-404" tabindex="-1">接口请求 404 <a class="header-anchor" href="#接口请求-404" aria-label="Permalink to &quot;接口请求 404&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查</strong>：</p>
<ol>
<li>检查 <code>.env</code> 中的 <code>VITE_GLOB_API_URL</code> 配置</li>
<li>检查网关是否正常运行</li>
<li>检查接口路径是否正确</li>
</ol>
<hr>
<h2 id="数据库" tabindex="-1">数据库 <a class="header-anchor" href="#数据库" aria-label="Permalink to &quot;数据库&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="数据库连接失败" tabindex="-1">数据库连接失败 <a class="header-anchor" href="#数据库连接失败" aria-label="Permalink to &quot;数据库连接失败&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：<code>Communications link failure</code> 或 <code>Access denied</code></p>
<p><strong>解决</strong>：</p>
<ul>
<li>检查 MySQL 服务是否启动</li>
<li>检查 Nacos 中的数据库配置（用户名、密码、端口）</li>
<li>确认 MySQL 允许远程连接</li>
</ul>
<h3 id="慢-sql-排查" tabindex="-1">慢 SQL 排查 <a class="header-anchor" href="#慢-sql-排查" aria-label="Permalink to &quot;慢 SQL 排查&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 开启慢查询日志</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> slow_query_log </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ON'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> long_query_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><hr>
<h2 id="多租户" tabindex="-1">多租户 <a class="header-anchor" href="#多租户" aria-label="Permalink to &quot;多租户&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="租户数据看不到" tabindex="-1">租户数据看不到 <a class="header-anchor" href="#租户数据看不到" aria-label="Permalink to &quot;租户数据看不到&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>现象</strong>：切换租户后查询不到数据</p>
<p><strong>解决</strong>：</p>
<ul>
<li>确认数据的 <code>tenant_id</code> 字段值正确</li>
<li>检查当前登录用户是否属于该租户</li>
<li>超级管理员账号可能不受租户限制</li>
</ul>
<h3 id="多租户数据隔离失效" tabindex="-1">多租户数据隔离失效 <a class="header-anchor" href="#多租户数据隔离失效" aria-label="Permalink to &quot;多租户数据隔离失效&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>排查</strong>：</p>
<ul>
<li>检查表是否有 <code>tenant_id</code> 字段</li>
<li>检查实体类是否继承 <code>TenantEntity</code></li>
<li>检查是否使用 <code>@IgnoreTenant</code> 注解跳过了租户过滤</li>
</ul>
<h3 id="某些表不需要租户隔离" tabindex="-1">某些表不需要租户隔离 <a class="header-anchor" href="#某些表不需要租户隔离" aria-label="Permalink to &quot;某些表不需要租户隔离&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 <code>@IgnoreTenant</code> 注解：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">IgnoreTenant</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Dict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> queryAllDict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="获取帮助" tabindex="-1">获取帮助 <a class="header-anchor" href="#获取帮助" aria-label="Permalink to &quot;获取帮助&quot;">&ZeroWidthSpace;</a></h2>
<p>如果以上文档无法解决您的问题：</p>
<ul>
<li>💬 <a href="https://gitee.com/battcn/wemirr-platform/issues" target="_blank" rel="noreferrer">问题反馈</a></li>
<li>📧 <a href="https://gitee.com/battcn/" target="_blank" rel="noreferrer">联系我们</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[最佳实践 ]]></title>
            <link>https://docs.battcn.com/zh/guide/advanced/best-practice.html</link>
            <guid>https://docs.battcn.com/zh/guide/advanced/best-practice.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 开发中的最佳实践和常见问题解决方案</p>
</div>
<h2 id="代码组织" tabindex="-1">代码组织 <a class="header-anchor" href="#代码组织" aria-label="Permalink to &quot;代码组织&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="分层架构" tabindex="-1">分层架构 <a class="header-anchor" href="#分层架构" aria-label="Permalink to &quot;分层架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Controller  - 参数校验、调用 Service、结果转换</span></span>
<span class="line"><span>    │</span></span>
<span class="line"><span>Service     - 业务逻辑、事务管理、调用其他 Service/Mapper</span></span>
<span class="line"><span>    │</span></span>
<span class="line"><span>Mapper      - 数据访问、SQL 操作</span></span>
<span class="line"><span>    │</span></span>
<span class="line"><span>Entity      - 数据库映射实体</span></span></code></pre>
</div><h3 id="包结构规范" tabindex="-1">包结构规范 <a class="header-anchor" href="#包结构规范" aria-label="Permalink to &quot;包结构规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>com.wemirr.xxx/</span></span>
<span class="line"><span>├── controller/           # 控制器</span></span>
<span class="line"><span>│   └── XxxController.java</span></span>
<span class="line"><span>├── service/              # 服务层</span></span>
<span class="line"><span>│   ├── XxxService.java</span></span>
<span class="line"><span>│   └── impl/</span></span>
<span class="line"><span>│       └── XxxServiceImpl.java</span></span>
<span class="line"><span>├── mapper/               # 数据访问层</span></span>
<span class="line"><span>│   └── XxxMapper.java</span></span>
<span class="line"><span>├── entity/               # 实体类</span></span>
<span class="line"><span>│   └── Xxx.java</span></span>
<span class="line"><span>├── dto/                  # 数据传输对象</span></span>
<span class="line"><span>│   ├── XxxDTO.java</span></span>
<span class="line"><span>│   └── XxxQuery.java</span></span>
<span class="line"><span>├── vo/                   # 视图对象</span></span>
<span class="line"><span>│   └── XxxVO.java</span></span>
<span class="line"><span>├── convert/              # 对象转换器</span></span>
<span class="line"><span>│   └── XxxConvert.java</span></span>
<span class="line"><span>├── enums/                # 枚举</span></span>
<span class="line"><span>│   └── XxxStatusEnum.java</span></span>
<span class="line"><span>└── constants/            # 常量</span></span>
<span class="line"><span>    └── XxxConstants.java</span></span></code></pre>
</div><h3 id="对象转换" tabindex="-1">对象转换 <a class="header-anchor" href="#对象转换" aria-label="Permalink to &quot;对象转换&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 MapStruct 进行对象转换</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">componentModel</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "spring"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderConvert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderConvert INSTANCE </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mappers.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderConvert.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toVOList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orders</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">target</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "statusName"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">expression</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "java(order.getStatus().getDesc())"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderDetailVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toDetailVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">OrderVO vo </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderConvert.INSTANCE.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span></code></pre>
</div><h2 id="异常处理" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="统一异常体系" tabindex="-1">统一异常体系 <a class="header-anchor" href="#统一异常体系" aria-label="Permalink to &quot;统一异常体系&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 业务异常基类</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RuntimeException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer code;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.BIZ_ERROR.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">resultCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(resultCode.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), resultCode.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Integer </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        super</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.code </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> code;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.message </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> message;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 结果码枚举</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ResultCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SUCCESS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    BIZ_ERROR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"业务异常"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    PARAM_ERROR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10001</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"参数错误"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    NOT_FOUND</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10002</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"资源不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    UNAUTHORIZED</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10003</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"未授权"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    FORBIDDEN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10004</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"禁止访问"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer code;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="断言式异常" tabindex="-1">断言式异常 <a class="header-anchor" href="#断言式异常" aria-label="Permalink to &quot;断言式异常&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 工具类</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizAssert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Object </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">obj</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (obj </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">boolean</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> expression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">expression) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> notEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collection&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">collection</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (collection </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> collection.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long orderId, OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">canUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单状态不允许修改"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="事务管理" tabindex="-1">事务管理 <a class="header-anchor" href="#事务管理" aria-label="Permalink to &quot;事务管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="事务注解使用" tabindex="-1">事务注解使用 <a class="header-anchor" href="#事务注解使用" aria-label="Permalink to &quot;事务注解使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 基本事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 所有操作在同一事务中</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 只读事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">readOnly</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 只读操作</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 新事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.REQUIRES_NEW)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> saveLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 无论外层事务是否回滚，日志都会保存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="事务失效场景" tabindex="-1">事务失效场景 <a class="header-anchor" href="#事务失效场景" aria-label="Permalink to &quot;事务失效场景&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 同类方法调用，事务失效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 事务失效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 解决方案1：注入自己</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderService self;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        self.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 事务生效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 解决方案2：使用 AopContext</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ((OrderService) AopContext.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentProxy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 异常被捕获，事务失效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"error"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, e);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 异常被吃掉，事务不会回滚</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 正确做法</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"error"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, e);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 重新抛出</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="安全实践" tabindex="-1">安全实践 <a class="header-anchor" href="#安全实践" aria-label="Permalink to &quot;安全实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="参数校验" tabindex="-1">参数校验 <a class="header-anchor" href="#参数校验" aria-label="Permalink to &quot;参数校验&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">min</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名长度2-20个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">regexp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "^[a-zA-Z0-9_]+$"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名只能包含字母、数字、下划线"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">min</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 6</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码长度6-20个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String password;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "邮箱格式不正确"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String email;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">regexp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "^1[3-9]</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">d{9}$"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号格式不正确"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Controller</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 参数已校验</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="sql-注入防护" tabindex="-1">SQL 注入防护 <a class="header-anchor" href="#sql-注入防护" aria-label="Permalink to &quot;SQL 注入防护&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 危险：字符串拼接</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String sql </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "SELECT * FROM user WHERE name = '"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 安全：使用参数绑定</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Select</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"SELECT * FROM user WHERE name = #{name}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) String name);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 安全：使用 Wrapper</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lambdaQuery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, name).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span></code></pre>
</div><h3 id="xss-防护" tabindex="-1">XSS 防护 <a class="header-anchor" href="#xss-防护" aria-label="Permalink to &quot;XSS 防护&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 配置 XSS 过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> XssConfig</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FilterRegistrationBean&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">XssFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">xssFilterRegistration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        FilterRegistrationBean&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">XssFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> registration </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FilterRegistrationBean&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        registration.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> XssFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        registration.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addUrlPatterns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        registration.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> registration;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 或使用 Hutool 的 HtmlUtil</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String safe </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> HtmlUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">escape</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userInput);</span></span></code></pre>
</div><h3 id="敏感数据脱敏" tabindex="-1">敏感数据脱敏 <a class="header-anchor" href="#敏感数据脱敏" aria-label="Permalink to &quot;敏感数据脱敏&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 返回 VO 时脱敏</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">JsonSerialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">using</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MobileSerializer.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 138****8888</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">JsonSerialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">using</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IdCardSerializer.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String idCard;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 110***********1234</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义序列化器</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MobileSerializer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> JsonSerializer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> serialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, JsonGenerator </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">gen</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, SerializerProvider </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">provider</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throws</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IOException {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isNotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> value.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 11</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            gen.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">writeString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">substring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "****"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> value.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">substring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            gen.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">writeString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="日志规范" tabindex="-1">日志规范 <a class="header-anchor" href="#日志规范" aria-label="Permalink to &quot;日志规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="日志级别" tabindex="-1">日志级别 <a class="header-anchor" href="#日志级别" aria-label="Permalink to &quot;日志级别&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>级别</th>
<th>使用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>ERROR</td>
<td>影响业务的错误</td>
</tr>
<tr>
<td>WARN</td>
<td>潜在问题、可恢复的错误</td>
</tr>
<tr>
<td>INFO</td>
<td>重要业务节点、状态变化</td>
</tr>
<tr>
<td>DEBUG</td>
<td>开发调试信息</td>
</tr>
<tr>
<td>TRACE</td>
<td>非常详细的调试信息</td>
</tr>
</tbody>
</table>
<h3 id="日志格式" tabindex="-1">日志格式 <a class="header-anchor" href="#日志格式" aria-label="Permalink to &quot;日志格式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 好的日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单成功, orderId={}, userId={}, amount={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId, userId, amount);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"调用支付接口失败, orderId={}, errorCode={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId, errorCode, e);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 不好的日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 缺少关键信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单成功, 订单: "</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字符串拼接性能差</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 丢失堆栈信息</span></span></code></pre>
</div><h3 id="日志配置" tabindex="-1">日志配置 <a class="header-anchor" href="#日志配置" aria-label="Permalink to &quot;日志配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- logback-spring.xml --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">configuration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">appender</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"FILE"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ch.qos.logback.core.rolling.RollingFileAppender"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>logs/app.log&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">rollingPolicy</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fileNamePattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>logs/app.%d{yyyy-MM-dd}.%i.log&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fileNamePattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxFileSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>100MB&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxFileSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxHistory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>30&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxHistory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">totalSizeCap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>10GB&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">totalSizeCap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">rollingPolicy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">encoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">encoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">appender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">configuration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="接口设计" tabindex="-1">接口设计 <a class="header-anchor" href="#接口设计" aria-label="Permalink to &quot;接口设计&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="restful-规范" tabindex="-1">RESTful 规范 <a class="header-anchor" href="#restful-规范" aria-label="Permalink to &quot;RESTful 规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>GET    /api/orders          # 列表查询</span></span>
<span class="line"><span>GET    /api/orders/{id}     # 单个查询</span></span>
<span class="line"><span>POST   /api/orders          # 创建</span></span>
<span class="line"><span>PUT    /api/orders/{id}     # 全量更新</span></span>
<span class="line"><span>PATCH  /api/orders/{id}     # 部分更新</span></span>
<span class="line"><span>DELETE /api/orders/{id}     # 删除</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 子资源</span></span>
<span class="line"><span>GET    /api/orders/{id}/items    # 获取订单项</span></span>
<span class="line"><span>POST   /api/orders/{id}/items    # 添加订单项</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 操作</span></span>
<span class="line"><span>POST   /api/orders/{id}/cancel   # 取消订单</span></span>
<span class="line"><span>POST   /api/orders/{id}/pay      # 支付订单</span></span></code></pre>
</div><h3 id="统一响应格式" tabindex="-1">统一响应格式 <a class="header-anchor" href="#统一响应格式" aria-label="Permalink to &quot;统一响应格式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Result</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer code;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> T data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long timestamp;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(T </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"success"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTimestamp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> result;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Integer </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTimestamp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> result;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="分页响应" tabindex="-1">分页响应 <a class="header-anchor" href="#分页响应" aria-label="Permalink to &quot;分页响应&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分页查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">OrderVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">defaultValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) Integer current,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">defaultValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "10"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) Integer size,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderQuery query</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(current, size, query);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(page);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 响应示例</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "code"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "message"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "success"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "data"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "records"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [...],</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "total"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "size"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "current"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "pages"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="测试实践" tabindex="-1">测试实践 <a class="header-anchor" href="#测试实践" aria-label="Permalink to &quot;测试实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="单元测试" tabindex="-1">单元测试 <a class="header-anchor" href="#单元测试" aria-label="Permalink to &quot;单元测试&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExtendWith</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(MockitoExtension.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderServiceTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mock</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderMapper orderMapper;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mock</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StockService stockService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">InjectMocks</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderServiceImpl orderService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Test</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder_Success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Given</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDTO dto </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        when</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stockService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">checkStock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">thenReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // When</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        orderService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Then</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        verify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderMapper).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order.class));</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        verify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stockService).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Test</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder_StockNotEnough</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Given</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDTO dto </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        when</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stockService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">checkStock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">thenReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // When &#x26; Then</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        assertThrows</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(BizException.class, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="接口测试" tabindex="-1">接口测试 <a class="header-anchor" href="#接口测试" aria-label="Permalink to &quot;接口测试&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SpringBootTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">webEnvironment</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WebEnvironment.RANDOM_PORT)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AutoConfigureMockMvc</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderControllerTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MockMvc mockMvc;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Test</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder_Success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throws</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDTO dto </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        mockMvc.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">perform</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/api/orders"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contentType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(MediaType.APPLICATION_JSON)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto)))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">andExpect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isOk</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">andExpect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">jsonPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"$.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-循环依赖" tabindex="-1">1. 循环依赖 <a class="header-anchor" href="#_1-循环依赖" aria-label="Permalink to &quot;1. 循环依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 循环依赖</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BService bService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AService aService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 解决方案：使用 @Lazy</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Lazy</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BService bService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_2-n-1-查询" tabindex="-1">2. N+1 查询 <a class="header-anchor" href="#_2-n-1-查询" aria-label="Permalink to &quot;2. N+1 查询&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ N+1 问题</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orders </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orders) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// N 次查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUserName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 批量查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orders </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Set&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIds </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orders.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUserId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">collect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collectors.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toSet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userMap </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectBatchIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userIds).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">collect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collectors.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, Function.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">identity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">orders.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forEach</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUserName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userMap.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/packages/devops/">运维部署</a> - 学习生产环境运维</li>
<li><a href="/zh/faq/">常见问题</a> - 查看更多问题解答</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 开发中的最佳实践和常见问题解决方案</p>
</div>
<h2 id="代码组织" tabindex="-1">代码组织 <a class="header-anchor" href="#代码组织" aria-label="Permalink to &quot;代码组织&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="分层架构" tabindex="-1">分层架构 <a class="header-anchor" href="#分层架构" aria-label="Permalink to &quot;分层架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Controller  - 参数校验、调用 Service、结果转换</span></span>
<span class="line"><span>    │</span></span>
<span class="line"><span>Service     - 业务逻辑、事务管理、调用其他 Service/Mapper</span></span>
<span class="line"><span>    │</span></span>
<span class="line"><span>Mapper      - 数据访问、SQL 操作</span></span>
<span class="line"><span>    │</span></span>
<span class="line"><span>Entity      - 数据库映射实体</span></span></code></pre>
</div><h3 id="包结构规范" tabindex="-1">包结构规范 <a class="header-anchor" href="#包结构规范" aria-label="Permalink to &quot;包结构规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>com.wemirr.xxx/</span></span>
<span class="line"><span>├── controller/           # 控制器</span></span>
<span class="line"><span>│   └── XxxController.java</span></span>
<span class="line"><span>├── service/              # 服务层</span></span>
<span class="line"><span>│   ├── XxxService.java</span></span>
<span class="line"><span>│   └── impl/</span></span>
<span class="line"><span>│       └── XxxServiceImpl.java</span></span>
<span class="line"><span>├── mapper/               # 数据访问层</span></span>
<span class="line"><span>│   └── XxxMapper.java</span></span>
<span class="line"><span>├── entity/               # 实体类</span></span>
<span class="line"><span>│   └── Xxx.java</span></span>
<span class="line"><span>├── dto/                  # 数据传输对象</span></span>
<span class="line"><span>│   ├── XxxDTO.java</span></span>
<span class="line"><span>│   └── XxxQuery.java</span></span>
<span class="line"><span>├── vo/                   # 视图对象</span></span>
<span class="line"><span>│   └── XxxVO.java</span></span>
<span class="line"><span>├── convert/              # 对象转换器</span></span>
<span class="line"><span>│   └── XxxConvert.java</span></span>
<span class="line"><span>├── enums/                # 枚举</span></span>
<span class="line"><span>│   └── XxxStatusEnum.java</span></span>
<span class="line"><span>└── constants/            # 常量</span></span>
<span class="line"><span>    └── XxxConstants.java</span></span></code></pre>
</div><h3 id="对象转换" tabindex="-1">对象转换 <a class="header-anchor" href="#对象转换" aria-label="Permalink to &quot;对象转换&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 MapStruct 进行对象转换</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">componentModel</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "spring"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderConvert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderConvert INSTANCE </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mappers.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderConvert.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toVOList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orders</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">target</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "statusName"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">expression</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "java(order.getStatus().getDesc())"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderDetailVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toDetailVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">OrderVO vo </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderConvert.INSTANCE.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span></code></pre>
</div><h2 id="异常处理" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="统一异常体系" tabindex="-1">统一异常体系 <a class="header-anchor" href="#统一异常体系" aria-label="Permalink to &quot;统一异常体系&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 业务异常基类</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RuntimeException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer code;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.BIZ_ERROR.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">resultCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(resultCode.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), resultCode.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Integer </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        super</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.code </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> code;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.message </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> message;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 结果码枚举</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ResultCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SUCCESS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    BIZ_ERROR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"业务异常"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    PARAM_ERROR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10001</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"参数错误"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    NOT_FOUND</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10002</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"资源不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    UNAUTHORIZED</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10003</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"未授权"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    FORBIDDEN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10004</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"禁止访问"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer code;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="断言式异常" tabindex="-1">断言式异常 <a class="header-anchor" href="#断言式异常" aria-label="Permalink to &quot;断言式异常&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 工具类</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizAssert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Object </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">obj</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (obj </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">boolean</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> expression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">expression) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> notEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collection&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">collection</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (collection </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> collection.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long orderId, OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">canUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单状态不允许修改"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="事务管理" tabindex="-1">事务管理 <a class="header-anchor" href="#事务管理" aria-label="Permalink to &quot;事务管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="事务注解使用" tabindex="-1">事务注解使用 <a class="header-anchor" href="#事务注解使用" aria-label="Permalink to &quot;事务注解使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 基本事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 所有操作在同一事务中</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 只读事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">readOnly</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 只读操作</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 新事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.REQUIRES_NEW)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> saveLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 无论外层事务是否回滚，日志都会保存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="事务失效场景" tabindex="-1">事务失效场景 <a class="header-anchor" href="#事务失效场景" aria-label="Permalink to &quot;事务失效场景&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 同类方法调用，事务失效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 事务失效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 解决方案1：注入自己</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderService self;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        self.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 事务生效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 解决方案2：使用 AopContext</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ((OrderService) AopContext.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentProxy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 异常被捕获，事务失效</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"error"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, e);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 异常被吃掉，事务不会回滚</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 正确做法</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"error"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, e);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 重新抛出</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="安全实践" tabindex="-1">安全实践 <a class="header-anchor" href="#安全实践" aria-label="Permalink to &quot;安全实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="参数校验" tabindex="-1">参数校验 <a class="header-anchor" href="#参数校验" aria-label="Permalink to &quot;参数校验&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">min</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名长度2-20个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">regexp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "^[a-zA-Z0-9_]+$"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名只能包含字母、数字、下划线"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">min</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 6</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码长度6-20个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String password;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "邮箱格式不正确"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String email;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">regexp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "^1[3-9]</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\\</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">d{9}$"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号格式不正确"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Controller</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 参数已校验</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="sql-注入防护" tabindex="-1">SQL 注入防护 <a class="header-anchor" href="#sql-注入防护" aria-label="Permalink to &quot;SQL 注入防护&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 危险：字符串拼接</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String sql </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "SELECT * FROM user WHERE name = '"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 安全：使用参数绑定</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Select</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"SELECT * FROM user WHERE name = #{name}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) String name);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 安全：使用 Wrapper</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lambdaQuery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, name).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span></code></pre>
</div><h3 id="xss-防护" tabindex="-1">XSS 防护 <a class="header-anchor" href="#xss-防护" aria-label="Permalink to &quot;XSS 防护&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 配置 XSS 过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> XssConfig</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FilterRegistrationBean&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">XssFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">xssFilterRegistration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        FilterRegistrationBean&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">XssFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> registration </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FilterRegistrationBean&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        registration.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> XssFilter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        registration.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addUrlPatterns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        registration.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> registration;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 或使用 Hutool 的 HtmlUtil</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String safe </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> HtmlUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">escape</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userInput);</span></span></code></pre>
</div><h3 id="敏感数据脱敏" tabindex="-1">敏感数据脱敏 <a class="header-anchor" href="#敏感数据脱敏" aria-label="Permalink to &quot;敏感数据脱敏&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 返回 VO 时脱敏</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">JsonSerialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">using</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MobileSerializer.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 138****8888</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">JsonSerialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">using</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IdCardSerializer.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String idCard;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 110***********1234</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义序列化器</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MobileSerializer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> JsonSerializer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> serialize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, JsonGenerator </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">gen</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, SerializerProvider </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">provider</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throws</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IOException {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isNotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> value.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 11</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            gen.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">writeString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">substring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "****"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> value.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">substring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            gen.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">writeString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="日志规范" tabindex="-1">日志规范 <a class="header-anchor" href="#日志规范" aria-label="Permalink to &quot;日志规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="日志级别" tabindex="-1">日志级别 <a class="header-anchor" href="#日志级别" aria-label="Permalink to &quot;日志级别&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>级别</th>
<th>使用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>ERROR</td>
<td>影响业务的错误</td>
</tr>
<tr>
<td>WARN</td>
<td>潜在问题、可恢复的错误</td>
</tr>
<tr>
<td>INFO</td>
<td>重要业务节点、状态变化</td>
</tr>
<tr>
<td>DEBUG</td>
<td>开发调试信息</td>
</tr>
<tr>
<td>TRACE</td>
<td>非常详细的调试信息</td>
</tr>
</tbody>
</table>
<h3 id="日志格式" tabindex="-1">日志格式 <a class="header-anchor" href="#日志格式" aria-label="Permalink to &quot;日志格式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 好的日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单成功, orderId={}, userId={}, amount={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId, userId, amount);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"调用支付接口失败, orderId={}, errorCode={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId, errorCode, e);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 不好的日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 缺少关键信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单成功, 订单: "</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字符串拼接性能差</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 丢失堆栈信息</span></span></code></pre>
</div><h3 id="日志配置" tabindex="-1">日志配置 <a class="header-anchor" href="#日志配置" aria-label="Permalink to &quot;日志配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- logback-spring.xml --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">configuration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">appender</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"FILE"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ch.qos.logback.core.rolling.RollingFileAppender"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>logs/app.log&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">rollingPolicy</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fileNamePattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>logs/app.%d{yyyy-MM-dd}.%i.log&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fileNamePattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxFileSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>100MB&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxFileSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxHistory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>30&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">maxHistory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">totalSizeCap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>10GB&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">totalSizeCap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">rollingPolicy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">encoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">encoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">appender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">configuration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="接口设计" tabindex="-1">接口设计 <a class="header-anchor" href="#接口设计" aria-label="Permalink to &quot;接口设计&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="restful-规范" tabindex="-1">RESTful 规范 <a class="header-anchor" href="#restful-规范" aria-label="Permalink to &quot;RESTful 规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>GET    /api/orders          # 列表查询</span></span>
<span class="line"><span>GET    /api/orders/{id}     # 单个查询</span></span>
<span class="line"><span>POST   /api/orders          # 创建</span></span>
<span class="line"><span>PUT    /api/orders/{id}     # 全量更新</span></span>
<span class="line"><span>PATCH  /api/orders/{id}     # 部分更新</span></span>
<span class="line"><span>DELETE /api/orders/{id}     # 删除</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 子资源</span></span>
<span class="line"><span>GET    /api/orders/{id}/items    # 获取订单项</span></span>
<span class="line"><span>POST   /api/orders/{id}/items    # 添加订单项</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 操作</span></span>
<span class="line"><span>POST   /api/orders/{id}/cancel   # 取消订单</span></span>
<span class="line"><span>POST   /api/orders/{id}/pay      # 支付订单</span></span></code></pre>
</div><h3 id="统一响应格式" tabindex="-1">统一响应格式 <a class="header-anchor" href="#统一响应格式" aria-label="Permalink to &quot;统一响应格式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Result</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer code;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String message;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> T data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long timestamp;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(T </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"success"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTimestamp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> result;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Integer </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">T</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;>();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTimestamp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> result;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="分页响应" tabindex="-1">分页响应 <a class="header-anchor" href="#分页响应" aria-label="Permalink to &quot;分页响应&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分页查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">OrderVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">defaultValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) Integer current,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">defaultValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "10"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) Integer size,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    OrderQuery query</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(current, size, query);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(page);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 响应示例</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "code"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "message"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "success"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "data"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "records"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [...],</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "total"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "size"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "current"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "pages"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="测试实践" tabindex="-1">测试实践 <a class="header-anchor" href="#测试实践" aria-label="Permalink to &quot;测试实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="单元测试" tabindex="-1">单元测试 <a class="header-anchor" href="#单元测试" aria-label="Permalink to &quot;单元测试&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExtendWith</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(MockitoExtension.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderServiceTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mock</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderMapper orderMapper;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mock</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StockService stockService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">InjectMocks</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderServiceImpl orderService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Test</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder_Success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Given</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDTO dto </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        when</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stockService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">checkStock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">thenReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // When</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        orderService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Then</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        verify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderMapper).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order.class));</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        verify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stockService).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Test</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder_StockNotEnough</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Given</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDTO dto </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        when</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stockService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">checkStock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">thenReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // When &#x26; Then</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        assertThrows</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(BizException.class, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="接口测试" tabindex="-1">接口测试 <a class="header-anchor" href="#接口测试" aria-label="Permalink to &quot;接口测试&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SpringBootTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">webEnvironment</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WebEnvironment.RANDOM_PORT)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AutoConfigureMockMvc</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderControllerTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MockMvc mockMvc;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Test</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder_Success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throws</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDTO dto </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        mockMvc.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">perform</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/api/orders"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contentType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(MediaType.APPLICATION_JSON)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto)))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">andExpect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isOk</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">andExpect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">jsonPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"$.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="常见问题" tabindex="-1">常见问题 <a class="header-anchor" href="#常见问题" aria-label="Permalink to &quot;常见问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-循环依赖" tabindex="-1">1. 循环依赖 <a class="header-anchor" href="#_1-循环依赖" aria-label="Permalink to &quot;1. 循环依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 循环依赖</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BService bService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AService aService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 解决方案：使用 @Lazy</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Lazy</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BService bService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_2-n-1-查询" tabindex="-1">2. N+1 查询 <a class="header-anchor" href="#_2-n-1-查询" aria-label="Permalink to &quot;2. N+1 查询&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ N+1 问题</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orders </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orders) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// N 次查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUserName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 批量查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orders </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Set&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIds </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orders.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUserId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">collect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collectors.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toSet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userMap </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectBatchIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userIds).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">collect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collectors.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, Function.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">identity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">orders.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forEach</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUserName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userMap.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/packages/devops/">运维部署</a> - 学习生产环境运维</li>
<li><a href="/zh/faq/">常见问题</a> - 查看更多问题解答</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[进阶开发概述 ]]></title>
            <link>https://docs.battcn.com/zh/guide/advanced/</link>
            <guid>https://docs.battcn.com/zh/guide/advanced/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="进阶开发概述" tabindex="-1">进阶开发概述 <a class="header-anchor" href="#进阶开发概述" aria-label="Permalink to &quot;进阶开发概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">适合人群</p>
<p>已掌握基础开发，希望深入了解架构设计和最佳实践的开发者</p>
</div>
<h2 id="进阶内容" tabindex="-1">进阶内容 <a class="header-anchor" href="#进阶内容" aria-label="Permalink to &quot;进阶内容&quot;">&ZeroWidthSpace;</a></h2>
<p>本章节涵盖以下进阶内容：</p>
<h3 id="架构设计" tabindex="-1">架构设计 <a class="header-anchor" href="#架构设计" aria-label="Permalink to &quot;架构设计&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>微服务拆分原则</strong> - 如何合理拆分服务边界</li>
<li><strong>领域驱动设计</strong> - DDD 在项目中的应用</li>
<li><strong>事件驱动架构</strong> - 使用消息队列解耦服务</li>
</ul>
<h3 id="性能优化" tabindex="-1">性能优化 <a class="header-anchor" href="#性能优化" aria-label="Permalink to &quot;性能优化&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>数据库优化</strong> - 索引、慢查询、分库分表</li>
<li><strong>缓存策略</strong> - 多级缓存、缓存一致性</li>
<li><strong>接口优化</strong> - 并发、异步、批量处理</li>
</ul>
<h3 id="安全加固" tabindex="-1">安全加固 <a class="header-anchor" href="#安全加固" aria-label="Permalink to &quot;安全加固&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>认证授权</strong> - Sa-Token 高级特性</li>
<li><strong>数据安全</strong> - 加密、脱敏、审计</li>
<li><strong>接口安全</strong> - 防重放、签名验证</li>
</ul>
<h3 id="运维部署" tabindex="-1">运维部署 <a class="header-anchor" href="#运维部署" aria-label="Permalink to &quot;运维部署&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>CI/CD</strong> - 自动化构建和部署</li>
<li><strong>监控告警</strong> - Prometheus + Grafana</li>
<li><strong>日志管理</strong> - ELK 日志收集</li>
</ul>
<h2 id="架构全景" tabindex="-1">架构全景 <a class="header-anchor" href="#架构全景" aria-label="Permalink to &quot;架构全景&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌──────────────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                         负载均衡 (Nginx/SLB)                      │</span></span>
<span class="line"><span>└─────────────────────────────┬────────────────────────────────────┘</span></span>
<span class="line"><span>                              │</span></span>
<span class="line"><span>┌─────────────────────────────▼────────────────────────────────────┐</span></span>
<span class="line"><span>│                         API 网关 (Gateway)                        │</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>│  IAM 认证中心  │    │   Suite 业务    │   │  Plugin 插件  │</span></span>
<span class="line"><span>│  ├─ 用户管理   │    │  ├─ 核心业务    │   │  ├─ BPM 审批  │</span></span>
<span class="line"><span>│  ├─ 权限管理   │    │  └─ 扩展业务    │   │  ├─ Job 任务  │</span></span>
<span class="line"><span>│  └─ 租户管理   │    │                 │   │  └─ AI 能力   │</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>│  │  Nacos   │ │  Redis   │ │  MySQL   │ │  MinIO   │            │</span></span>
<span class="line"><span>│  │ 注册配置  │ │   缓存   │ │  数据库  │ │ 文件存储  │            │</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;">&ZeroWidthSpace;</a></h2>
<h3 id="框架选择" tabindex="-1">框架选择 <a class="header-anchor" href="#框架选择" aria-label="Permalink to &quot;框架选择&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>场景</th>
<th>推荐方案</th>
<th>备选方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>服务通信</td>
<td>OpenFeign</td>
<td>Dubbo</td>
</tr>
<tr>
<td>配置中心</td>
<td>Nacos</td>
<td>Apollo</td>
</tr>
<tr>
<td>注册中心</td>
<td>Nacos</td>
<td>Eureka</td>
</tr>
<tr>
<td>网关</td>
<td>Spring Cloud Gateway</td>
<td>Zuul</td>
</tr>
<tr>
<td>限流熔断</td>
<td>Sentinel</td>
<td>Resilience4j</td>
</tr>
<tr>
<td>分布式事务</td>
<td>Seata</td>
<td>RocketMQ 事务消息</td>
</tr>
<tr>
<td>定时任务</td>
<td>Snail Job</td>
<td>XXL-Job</td>
</tr>
<tr>
<td>审批流程</td>
<td>Warm-Flow</td>
<td>Camunda</td>
</tr>
</tbody>
</table>
<h3 id="数据库选择" tabindex="-1">数据库选择 <a class="header-anchor" href="#数据库选择" aria-label="Permalink to &quot;数据库选择&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>场景</th>
<th>推荐方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>业务数据</td>
<td>MySQL 8.0</td>
</tr>
<tr>
<td>缓存数据</td>
<td>Redis 6+</td>
</tr>
<tr>
<td>文档存储</td>
<td>MongoDB</td>
</tr>
<tr>
<td>搜索引擎</td>
<td>Elasticsearch</td>
</tr>
<tr>
<td>时序数据</td>
<td>InfluxDB / TDengine</td>
</tr>
</tbody>
</table>
<h2 id="多租户架构-真实代码" tabindex="-1">多租户架构（真实代码） <a class="header-anchor" href="#多租户架构-真实代码" aria-label="Permalink to &quot;多租户架构（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 支持三种多租户模式，通过 <code>DatabaseProperties</code> 配置：</p>
<h3 id="多租户模式" tabindex="-1">多租户模式 <a class="header-anchor" href="#多租户模式" aria-label="Permalink to &quot;多租户模式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 MultiTenantType.java</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MultiTenantType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    NONE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"非租户模式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 不启用多租户</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    COLUMN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"字段模式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 通过 tenant_id 字段隔离（推荐，数据量 &#x3C; 1000w）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    DATASOURCE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"独立数据源模式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 每个租户独立数据库（大客户）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="配置示例" tabindex="-1">配置示例 <a class="header-anchor" href="#配置示例" aria-label="Permalink to &quot;配置示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">COLUMN</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                    # 租户模式：NONE/COLUMN/DATASOURCE</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      super-tenant-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0000"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 超级租户编码</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      tenant-id-column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"tenant_id"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 租户字段名</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      include-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 需要租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_user</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_role</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_org</span></span></code></pre>
</div><h3 id="tenanthelper-工具类" tabindex="-1">TenantHelper 工具类 <a class="header-anchor" href="#tenanthelper-工具类" aria-label="Permalink to &quot;TenantHelper 工具类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantHelper.java - 租户操作工具</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 判断是否是超级租户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> isSuper </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用主数据源执行（查询平台级数据）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> dictList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithMaster</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(SysDict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getType, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换到指定租户数据源执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8888"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 临时忽略租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">withIgnoreStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h3 id="租户拦截器原理" tabindex="-1">租户拦截器原理 <a class="header-anchor" href="#租户拦截器原理" aria-label="Permalink to &quot;租户拦截器原理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 BaseMybatisConfiguration.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">interceptor.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantLineInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantLineHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 自动获取当前登录用户的租户ID</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> LongValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ignoreTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 匿名用户或不在配置表中的表，不添加租户过滤</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">anonymous</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">tables.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tableName);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}));</span></span></code></pre>
</div><hr>
<h2 id="数据权限-真实代码" tabindex="-1">数据权限（真实代码） <a class="header-anchor" href="#数据权限-真实代码" aria-label="Permalink to &quot;数据权限（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架内置数据权限支持，支持多维度控制。</p>
<h3 id="数据权限类型" tabindex="-1">数据权限类型 <a class="header-anchor" href="#数据权限类型" aria-label="Permalink to &quot;数据权限类型&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeType.java</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    ALL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 全部数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,         </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 本级数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL_CHILDREN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 本级及子级</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    CUSTOMIZE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SELF</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                // 仅本人</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="数据权限实现" tabindex="-1">数据权限实现 <a class="header-anchor" href="#数据权限实现" aria-label="Permalink to &quot;数据权限实现&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataScopeById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long userId, Long orgId) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Role</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">findRoleByUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 找到权限最大的角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Role role </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Comparator.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">comparingInt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CUSTOMIZE) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 自定义：查询角色关联的组织下的用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dataPermissionRefMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(...)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataPermissionRef</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getDataId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 本级：只查询同组织用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgId))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL_CHILDREN) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 本级及子级</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orgService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFullTreeIdPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orgId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER, userIdList);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用数据权限" tabindex="-1">使用数据权限 <a class="header-anchor" href="#使用数据权限" aria-label="Permalink to &quot;使用数据权限&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 在 Service 中使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RemoteResult</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserPageResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserPageReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 使用 DataPermissionUtils 包装查询，自动应用数据权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), wrapper)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="分布式锁-真实代码" tabindex="-1">分布式锁（真实代码） <a class="header-anchor" href="#分布式锁-真实代码" aria-label="Permalink to &quot;分布式锁（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架提供注解式分布式锁，基于 Redisson 实现。</p>
<h3 id="redislock-注解" tabindex="-1">@RedisLock 注解 <a class="header-anchor" href="#redislock-注解" aria-label="Permalink to &quot;@RedisLock 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 RedisLockInterceptor.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "order:create"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 锁前缀</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    delimiter</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ":"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分隔符</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    expire</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 过期时间（秒）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    waitTime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,               </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 等待时间（秒）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    timeUnit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TimeUnit.SECONDS,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    lockType</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LockType.REENTRANT_LOCK,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 锁类型</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "操作过于频繁，请稍后重试"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 自动加锁，方法执行完自动释放</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="锁类型" tabindex="-1">锁类型 <a class="header-anchor" href="#锁类型" aria-label="Permalink to &quot;锁类型&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> LockType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    REENTRANT_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 可重入锁（默认）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    FAIR_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 公平锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    READ_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 读锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    WRITE_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 写锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    RED_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 红锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    MULTI_LOCK</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       // 联锁</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="支持-spel-表达式" tabindex="-1">支持 SpEL 表达式 <a class="header-anchor" href="#支持-spel-表达式" aria-label="Permalink to &quot;支持 SpEL 表达式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 动态锁 key</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "'order:' + #dto.userId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">expire</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 锁 key = order:{userId}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="动态数据源" tabindex="-1">动态数据源 <a class="header-anchor" href="#动态数据源" aria-label="Permalink to &quot;动态数据源&quot;">&ZeroWidthSpace;</a></h2>
<p>独立数据源模式下，框架自动切换租户数据源。</p>
<h3 id="自动切换原理" tabindex="-1">自动切换原理 <a class="header-anchor" href="#自动切换原理" aria-label="Permalink to &quot;自动切换原理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DynamicDataSourceWebMvcAutoConfiguration.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> preHandle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HttpServletRequest request, ...) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String tenantCode </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String dsKey;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantCode)) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 超级租户使用主库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dsKey </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDefaultDsName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// master</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 普通租户使用租户库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dsKey </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDsPrefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantCode;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// wemirr_tenant_8888</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DynamicDataSourceContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dsKey);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="差异日志-真实代码" tabindex="-1">差异日志（真实代码） <a class="header-anchor" href="#差异日志-真实代码" aria-label="Permalink to &quot;差异日志（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架内置差异日志功能，自动记录数据修改前后的变化。</p>
<h3 id="difflog-注解" tabindex="-1">@DiffLog 注解 <a class="header-anchor" href="#difflog-注解" aria-label="Permalink to &quot;@DiffLog 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DiffLog.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    group</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 业务分组</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编辑用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 操作标签</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    businessKey</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "{{#id}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 业务标识（SpEL）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    success</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息 {_DIFF{#_newObj}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 成功日志</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    fail</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息异常 {{#id}}"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 失败日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id, UserUpdateReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User oldUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User newUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 设置差异对比对象</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DiffLogContext.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">putDiffItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(oldUser, newUser);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(newUser);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动记录字段变化" tabindex="-1">自动记录字段变化 <a class="header-anchor" href="#自动记录字段变化" aria-label="Permalink to &quot;自动记录字段变化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 在实体字段上标记需要对比的字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">strategy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DiffFieldStrategy.NOT_NULL)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>日志输出示例：<code>更新用户信息 【用户名】由【张三】改为【李四】，【手机号】由【138****8888】改为【139****9999】</code></p>
<hr>
<h2 id="异步处理-真实代码" tabindex="-1">异步处理（真实代码） <a class="header-anchor" href="#异步处理-真实代码" aria-label="Permalink to &quot;异步处理（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架内置异步线程池配置，支持 <code>@Async</code> 注解。</p>
<h3 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      core-pool-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 核心线程数</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      max-pool-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 最大线程数</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      queue-capacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 队列容量</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      keep-alive-seconds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 线程存活时间</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      thread-name-prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"wemirr-async-thread-"</span></span></code></pre>
</div><h3 id="使用" tabindex="-1">使用 <a class="header-anchor" href="#使用" aria-label="Permalink to &quot;使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> NotificationService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> EmailService emailService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SmsService smsService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 异步发送通知，不阻塞主流程</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendNotification</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        emailService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        smsService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="上下文传递" tabindex="-1">上下文传递 <a class="header-anchor" href="#上下文传递" aria-label="Permalink to &quot;上下文传递&quot;">&ZeroWidthSpace;</a></h3>
<p>框架自动处理异步线程的 Request 上下文复制：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 AsyncConfiguration.java - 自动复制请求上下文</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RequestAttributesTaskDecorator</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TaskDecorator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Runnable </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">decorate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Runnable </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">runnable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ServletRequestAttributes attributes </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            (ServletRequestAttributes) RequestContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequestAttributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                RequestContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setRequestAttributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(attributes);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                runnable.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                RequestContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resetRequestAttributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="缓存配置-真实代码" tabindex="-1">缓存配置（真实代码） <a class="header-anchor" href="#缓存配置-真实代码" aria-label="Permalink to &quot;缓存配置（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架基于 Spring Cache + Redis 实现缓存功能。</p>
<h3 id="配置-1" tabindex="-1">配置 <a class="header-anchor" href="#配置-1" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    cache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"wemirr_cache_"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 默认 24 小时</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      items</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">user</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3600</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 用户缓存 1 小时</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">dict</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 字典缓存 24 小时</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h3 id="使用-1" tabindex="-1">使用 <a class="header-anchor" href="#使用-1" aria-label="Permalink to &quot;使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DictService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查询时缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Cacheable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时清除缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时更新缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CachePut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DictDTO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateAndReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="feign-远程调用" tabindex="-1">Feign 远程调用 <a class="header-anchor" href="#feign-远程调用" aria-label="Permalink to &quot;Feign 远程调用&quot;">&ZeroWidthSpace;</a></h2>
<p>框架封装了 Feign 调用，自动传递认证信息和租户信息。</p>
<div class="tip custom-block"><p class="custom-block-title">官方文档</p>
<p>📖 <strong>Spring Cloud OpenFeign</strong>：<a href="https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/" target="_blank" rel="noreferrer">https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/</a></p>
</div>
<h3 id="定义-feign-client" tabindex="-1">定义 Feign Client <a class="header-anchor" href="#定义-feign-client" aria-label="Permalink to &quot;定义 Feign Client&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "wemirr-platform-iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IamFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    UserVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/batch_ids"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">batchGetUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Set&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ids</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动传递-header" tabindex="-1">自动传递 Header <a class="header-anchor" href="#自动传递-header" aria-label="Permalink to &quot;自动传递 Header&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 FeignPluginInterceptor.java - 自动传递认证信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> apply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RequestTemplate template) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 传递 Token</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">header</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Authorization"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Bearer "</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> token);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 传递租户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">header</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Tenant-Code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, tenantCode);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 传递追踪ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">header</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Trace-Id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, traceId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="remoteresult-自动填充" tabindex="-1">@RemoteResult 自动填充 <a class="header-anchor" href="#remoteresult-自动填充" aria-label="Permalink to &quot;@RemoteResult 自动填充&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RemoteResult</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 自动填充远程数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">OrderVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderPageReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查询订单列表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(...);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // @RemoteResult 会自动调用 Feign 填充 userName 等远程字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> page;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="操作日志" tabindex="-1">操作日志 <a class="header-anchor" href="#操作日志" aria-label="Permalink to &quot;操作日志&quot;">&ZeroWidthSpace;</a></h2>
<p>框架提供 <code>@AccessLog</code> 注解记录操作日志。</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/create"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserSaveReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>自动记录：IP、URI、用户、耗时、参数等信息。</p>
<hr>
<h2 id="开发规范" tabindex="-1">开发规范 <a class="header-anchor" href="#开发规范" aria-label="Permalink to &quot;开发规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="代码规范" tabindex="-1">代码规范 <a class="header-anchor" href="#代码规范" aria-label="Permalink to &quot;代码规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java - 真实的服务实现规范</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DatabaseProperties properties;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 1. 业务校验</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户名称重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCode, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户编码重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 2. 数据转换</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, Tenant.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 3. 持久化</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异常处理规范" tabindex="-1">异常处理规范 <a class="header-anchor" href="#异常处理规范" aria-label="Permalink to &quot;异常处理规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 CheckedException.java - 框架统一异常</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 400 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户名称重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单 {0} 不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 404 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 403 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forbidden</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"登录过期,请重新登录"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 Optional 优雅处理</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span></code></pre>
</div><h2 id="学习路线" tabindex="-1">学习路线 <a class="header-anchor" href="#学习路线" aria-label="Permalink to &quot;学习路线&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>入门阶段（1-2周）</span></span>
<span class="line"><span>├─ 环境搭建</span></span>
<span class="line"><span>├─ 项目运行</span></span>
<span class="line"><span>└─ 基础 CRUD</span></span>
<span class="line"><span></span></span>
<span class="line"><span>进阶阶段（2-4周）</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>精通阶段（4周+）</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></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/advanced/performance.html">性能优化</a> - 学习性能优化技巧</li>
<li><a href="/zh/guide/advanced/best-practice.html">最佳实践</a> - 了解开发最佳实践</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="进阶开发概述" tabindex="-1">进阶开发概述 <a class="header-anchor" href="#进阶开发概述" aria-label="Permalink to &quot;进阶开发概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">适合人群</p>
<p>已掌握基础开发，希望深入了解架构设计和最佳实践的开发者</p>
</div>
<h2 id="进阶内容" tabindex="-1">进阶内容 <a class="header-anchor" href="#进阶内容" aria-label="Permalink to &quot;进阶内容&quot;">&ZeroWidthSpace;</a></h2>
<p>本章节涵盖以下进阶内容：</p>
<h3 id="架构设计" tabindex="-1">架构设计 <a class="header-anchor" href="#架构设计" aria-label="Permalink to &quot;架构设计&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>微服务拆分原则</strong> - 如何合理拆分服务边界</li>
<li><strong>领域驱动设计</strong> - DDD 在项目中的应用</li>
<li><strong>事件驱动架构</strong> - 使用消息队列解耦服务</li>
</ul>
<h3 id="性能优化" tabindex="-1">性能优化 <a class="header-anchor" href="#性能优化" aria-label="Permalink to &quot;性能优化&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>数据库优化</strong> - 索引、慢查询、分库分表</li>
<li><strong>缓存策略</strong> - 多级缓存、缓存一致性</li>
<li><strong>接口优化</strong> - 并发、异步、批量处理</li>
</ul>
<h3 id="安全加固" tabindex="-1">安全加固 <a class="header-anchor" href="#安全加固" aria-label="Permalink to &quot;安全加固&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>认证授权</strong> - Sa-Token 高级特性</li>
<li><strong>数据安全</strong> - 加密、脱敏、审计</li>
<li><strong>接口安全</strong> - 防重放、签名验证</li>
</ul>
<h3 id="运维部署" tabindex="-1">运维部署 <a class="header-anchor" href="#运维部署" aria-label="Permalink to &quot;运维部署&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>CI/CD</strong> - 自动化构建和部署</li>
<li><strong>监控告警</strong> - Prometheus + Grafana</li>
<li><strong>日志管理</strong> - ELK 日志收集</li>
</ul>
<h2 id="架构全景" tabindex="-1">架构全景 <a class="header-anchor" href="#架构全景" aria-label="Permalink to &quot;架构全景&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌──────────────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                         负载均衡 (Nginx/SLB)                      │</span></span>
<span class="line"><span>└─────────────────────────────┬────────────────────────────────────┘</span></span>
<span class="line"><span>                              │</span></span>
<span class="line"><span>┌─────────────────────────────▼────────────────────────────────────┐</span></span>
<span class="line"><span>│                         API 网关 (Gateway)                        │</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>│  IAM 认证中心  │    │   Suite 业务    │   │  Plugin 插件  │</span></span>
<span class="line"><span>│  ├─ 用户管理   │    │  ├─ 核心业务    │   │  ├─ BPM 审批  │</span></span>
<span class="line"><span>│  ├─ 权限管理   │    │  └─ 扩展业务    │   │  ├─ Job 任务  │</span></span>
<span class="line"><span>│  └─ 租户管理   │    │                 │   │  └─ AI 能力   │</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>│  │  Nacos   │ │  Redis   │ │  MySQL   │ │  MinIO   │            │</span></span>
<span class="line"><span>│  │ 注册配置  │ │   缓存   │ │  数据库  │ │ 文件存储  │            │</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;">&ZeroWidthSpace;</a></h2>
<h3 id="框架选择" tabindex="-1">框架选择 <a class="header-anchor" href="#框架选择" aria-label="Permalink to &quot;框架选择&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>场景</th>
<th>推荐方案</th>
<th>备选方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>服务通信</td>
<td>OpenFeign</td>
<td>Dubbo</td>
</tr>
<tr>
<td>配置中心</td>
<td>Nacos</td>
<td>Apollo</td>
</tr>
<tr>
<td>注册中心</td>
<td>Nacos</td>
<td>Eureka</td>
</tr>
<tr>
<td>网关</td>
<td>Spring Cloud Gateway</td>
<td>Zuul</td>
</tr>
<tr>
<td>限流熔断</td>
<td>Sentinel</td>
<td>Resilience4j</td>
</tr>
<tr>
<td>分布式事务</td>
<td>Seata</td>
<td>RocketMQ 事务消息</td>
</tr>
<tr>
<td>定时任务</td>
<td>Snail Job</td>
<td>XXL-Job</td>
</tr>
<tr>
<td>审批流程</td>
<td>Warm-Flow</td>
<td>Camunda</td>
</tr>
</tbody>
</table>
<h3 id="数据库选择" tabindex="-1">数据库选择 <a class="header-anchor" href="#数据库选择" aria-label="Permalink to &quot;数据库选择&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>场景</th>
<th>推荐方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>业务数据</td>
<td>MySQL 8.0</td>
</tr>
<tr>
<td>缓存数据</td>
<td>Redis 6+</td>
</tr>
<tr>
<td>文档存储</td>
<td>MongoDB</td>
</tr>
<tr>
<td>搜索引擎</td>
<td>Elasticsearch</td>
</tr>
<tr>
<td>时序数据</td>
<td>InfluxDB / TDengine</td>
</tr>
</tbody>
</table>
<h2 id="多租户架构-真实代码" tabindex="-1">多租户架构（真实代码） <a class="header-anchor" href="#多租户架构-真实代码" aria-label="Permalink to &quot;多租户架构（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 支持三种多租户模式，通过 <code>DatabaseProperties</code> 配置：</p>
<h3 id="多租户模式" tabindex="-1">多租户模式 <a class="header-anchor" href="#多租户模式" aria-label="Permalink to &quot;多租户模式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 MultiTenantType.java</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MultiTenantType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    NONE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"非租户模式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 不启用多租户</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    COLUMN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"字段模式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 通过 tenant_id 字段隔离（推荐，数据量 &#x3C; 1000w）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    DATASOURCE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"独立数据源模式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 每个租户独立数据库（大客户）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="配置示例" tabindex="-1">配置示例 <a class="header-anchor" href="#配置示例" aria-label="Permalink to &quot;配置示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">COLUMN</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                    # 租户模式：NONE/COLUMN/DATASOURCE</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      super-tenant-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0000"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 超级租户编码</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      tenant-id-column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"tenant_id"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 租户字段名</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      include-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 需要租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_user</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_role</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_org</span></span></code></pre>
</div><h3 id="tenanthelper-工具类" tabindex="-1">TenantHelper 工具类 <a class="header-anchor" href="#tenanthelper-工具类" aria-label="Permalink to &quot;TenantHelper 工具类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantHelper.java - 租户操作工具</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 判断是否是超级租户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> isSuper </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用主数据源执行（查询平台级数据）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> dictList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithMaster</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(SysDict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getType, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换到指定租户数据源执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8888"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 临时忽略租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">withIgnoreStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h3 id="租户拦截器原理" tabindex="-1">租户拦截器原理 <a class="header-anchor" href="#租户拦截器原理" aria-label="Permalink to &quot;租户拦截器原理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 BaseMybatisConfiguration.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">interceptor.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantLineInnerInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantLineHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 自动获取当前登录用户的租户ID</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> LongValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ignoreTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 匿名用户或不在配置表中的表，不添加租户过滤</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">anonymous</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">tables.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tableName);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}));</span></span></code></pre>
</div><hr>
<h2 id="数据权限-真实代码" tabindex="-1">数据权限（真实代码） <a class="header-anchor" href="#数据权限-真实代码" aria-label="Permalink to &quot;数据权限（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架内置数据权限支持，支持多维度控制。</p>
<h3 id="数据权限类型" tabindex="-1">数据权限类型 <a class="header-anchor" href="#数据权限类型" aria-label="Permalink to &quot;数据权限类型&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeType.java</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    ALL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 全部数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,         </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 本级数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL_CHILDREN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 本级及子级</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    CUSTOMIZE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SELF</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                // 仅本人</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="数据权限实现" tabindex="-1">数据权限实现 <a class="header-anchor" href="#数据权限实现" aria-label="Permalink to &quot;数据权限实现&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataScopeById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long userId, Long orgId) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Role</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">findRoleByUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 找到权限最大的角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Role role </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Comparator.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">comparingInt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CUSTOMIZE) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 自定义：查询角色关联的组织下的用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dataPermissionRefMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(...)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataPermissionRef</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getDataId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 本级：只查询同组织用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgId))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL_CHILDREN) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 本级及子级</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orgService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFullTreeIdPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orgId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER, userIdList);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用数据权限" tabindex="-1">使用数据权限 <a class="header-anchor" href="#使用数据权限" aria-label="Permalink to &quot;使用数据权限&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 在 Service 中使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RemoteResult</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserPageResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserPageReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 使用 DataPermissionUtils 包装查询，自动应用数据权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), wrapper)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="分布式锁-真实代码" tabindex="-1">分布式锁（真实代码） <a class="header-anchor" href="#分布式锁-真实代码" aria-label="Permalink to &quot;分布式锁（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架提供注解式分布式锁，基于 Redisson 实现。</p>
<h3 id="redislock-注解" tabindex="-1">@RedisLock 注解 <a class="header-anchor" href="#redislock-注解" aria-label="Permalink to &quot;@RedisLock 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 RedisLockInterceptor.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "order:create"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 锁前缀</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    delimiter</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ":"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分隔符</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    expire</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 过期时间（秒）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    waitTime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,               </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 等待时间（秒）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    timeUnit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TimeUnit.SECONDS,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    lockType</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LockType.REENTRANT_LOCK,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 锁类型</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "操作过于频繁，请稍后重试"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 自动加锁，方法执行完自动释放</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="锁类型" tabindex="-1">锁类型 <a class="header-anchor" href="#锁类型" aria-label="Permalink to &quot;锁类型&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> LockType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    REENTRANT_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 可重入锁（默认）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    FAIR_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 公平锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    READ_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 读锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    WRITE_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 写锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    RED_LOCK</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 红锁</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    MULTI_LOCK</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       // 联锁</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="支持-spel-表达式" tabindex="-1">支持 SpEL 表达式 <a class="header-anchor" href="#支持-spel-表达式" aria-label="Permalink to &quot;支持 SpEL 表达式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 动态锁 key</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "'order:' + #dto.userId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">expire</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 锁 key = order:{userId}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="动态数据源" tabindex="-1">动态数据源 <a class="header-anchor" href="#动态数据源" aria-label="Permalink to &quot;动态数据源&quot;">&ZeroWidthSpace;</a></h2>
<p>独立数据源模式下，框架自动切换租户数据源。</p>
<h3 id="自动切换原理" tabindex="-1">自动切换原理 <a class="header-anchor" href="#自动切换原理" aria-label="Permalink to &quot;自动切换原理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DynamicDataSourceWebMvcAutoConfiguration.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> preHandle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HttpServletRequest request, ...) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String tenantCode </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String dsKey;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantCode)) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 超级租户使用主库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dsKey </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDefaultDsName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// master</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 普通租户使用租户库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dsKey </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDsPrefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantCode;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// wemirr_tenant_8888</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DynamicDataSourceContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dsKey);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="差异日志-真实代码" tabindex="-1">差异日志（真实代码） <a class="header-anchor" href="#差异日志-真实代码" aria-label="Permalink to &quot;差异日志（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架内置差异日志功能，自动记录数据修改前后的变化。</p>
<h3 id="difflog-注解" tabindex="-1">@DiffLog 注解 <a class="header-anchor" href="#difflog-注解" aria-label="Permalink to &quot;@DiffLog 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DiffLog.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    group</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 业务分组</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编辑用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 操作标签</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    businessKey</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "{{#id}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 业务标识（SpEL）</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    success</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息 {_DIFF{#_newObj}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 成功日志</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    fail</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息异常 {{#id}}"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 失败日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id, UserUpdateReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User oldUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User newUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 设置差异对比对象</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DiffLogContext.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">putDiffItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(oldUser, newUser);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(newUser);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动记录字段变化" tabindex="-1">自动记录字段变化 <a class="header-anchor" href="#自动记录字段变化" aria-label="Permalink to &quot;自动记录字段变化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 在实体字段上标记需要对比的字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">strategy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DiffFieldStrategy.NOT_NULL)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>日志输出示例：<code>更新用户信息 【用户名】由【张三】改为【李四】，【手机号】由【138****8888】改为【139****9999】</code></p>
<hr>
<h2 id="异步处理-真实代码" tabindex="-1">异步处理（真实代码） <a class="header-anchor" href="#异步处理-真实代码" aria-label="Permalink to &quot;异步处理（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架内置异步线程池配置，支持 <code>@Async</code> 注解。</p>
<h3 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      core-pool-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 核心线程数</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      max-pool-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 最大线程数</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      queue-capacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 队列容量</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      keep-alive-seconds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 线程存活时间</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      thread-name-prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"wemirr-async-thread-"</span></span></code></pre>
</div><h3 id="使用" tabindex="-1">使用 <a class="header-anchor" href="#使用" aria-label="Permalink to &quot;使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> NotificationService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> EmailService emailService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SmsService smsService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 异步发送通知，不阻塞主流程</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendNotification</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        emailService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        smsService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="上下文传递" tabindex="-1">上下文传递 <a class="header-anchor" href="#上下文传递" aria-label="Permalink to &quot;上下文传递&quot;">&ZeroWidthSpace;</a></h3>
<p>框架自动处理异步线程的 Request 上下文复制：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 AsyncConfiguration.java - 自动复制请求上下文</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RequestAttributesTaskDecorator</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TaskDecorator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Runnable </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">decorate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Runnable </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">runnable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ServletRequestAttributes attributes </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            (ServletRequestAttributes) RequestContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequestAttributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                RequestContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setRequestAttributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(attributes);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                runnable.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                RequestContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resetRequestAttributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="缓存配置-真实代码" tabindex="-1">缓存配置（真实代码） <a class="header-anchor" href="#缓存配置-真实代码" aria-label="Permalink to &quot;缓存配置（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<p>框架基于 Spring Cache + Redis 实现缓存功能。</p>
<h3 id="配置-1" tabindex="-1">配置 <a class="header-anchor" href="#配置-1" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    cache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"wemirr_cache_"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 默认 24 小时</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      items</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">user</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3600</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 用户缓存 1 小时</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">dict</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 字典缓存 24 小时</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h3 id="使用-1" tabindex="-1">使用 <a class="header-anchor" href="#使用-1" aria-label="Permalink to &quot;使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DictService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查询时缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Cacheable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时清除缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时更新缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CachePut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DictDTO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateAndReturn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="feign-远程调用" tabindex="-1">Feign 远程调用 <a class="header-anchor" href="#feign-远程调用" aria-label="Permalink to &quot;Feign 远程调用&quot;">&ZeroWidthSpace;</a></h2>
<p>框架封装了 Feign 调用，自动传递认证信息和租户信息。</p>
<div class="tip custom-block"><p class="custom-block-title">官方文档</p>
<p>📖 <strong>Spring Cloud OpenFeign</strong>：<a href="https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/" target="_blank" rel="noreferrer">https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/</a></p>
</div>
<h3 id="定义-feign-client" tabindex="-1">定义 Feign Client <a class="header-anchor" href="#定义-feign-client" aria-label="Permalink to &quot;定义 Feign Client&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "wemirr-platform-iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IamFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    UserVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/batch_ids"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">batchGetUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Set&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ids</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动传递-header" tabindex="-1">自动传递 Header <a class="header-anchor" href="#自动传递-header" aria-label="Permalink to &quot;自动传递 Header&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 FeignPluginInterceptor.java - 自动传递认证信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> apply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RequestTemplate template) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 传递 Token</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">header</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Authorization"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Bearer "</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> token);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 传递租户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">header</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Tenant-Code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, tenantCode);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 传递追踪ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    template.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">header</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Trace-Id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, traceId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="remoteresult-自动填充" tabindex="-1">@RemoteResult 自动填充 <a class="header-anchor" href="#remoteresult-自动填充" aria-label="Permalink to &quot;@RemoteResult 自动填充&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RemoteResult</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 自动填充远程数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">OrderVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderPageReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查询订单列表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(...);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // @RemoteResult 会自动调用 Feign 填充 userName 等远程字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> page;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="操作日志" tabindex="-1">操作日志 <a class="header-anchor" href="#操作日志" aria-label="Permalink to &quot;操作日志&quot;">&ZeroWidthSpace;</a></h2>
<p>框架提供 <code>@AccessLog</code> 注解记录操作日志。</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/create"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserSaveReq req) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>自动记录：IP、URI、用户、耗时、参数等信息。</p>
<hr>
<h2 id="开发规范" tabindex="-1">开发规范 <a class="header-anchor" href="#开发规范" aria-label="Permalink to &quot;开发规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="代码规范" tabindex="-1">代码规范 <a class="header-anchor" href="#代码规范" aria-label="Permalink to &quot;代码规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java - 真实的服务实现规范</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DatabaseProperties properties;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 1. 业务校验</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户名称重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCode, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户编码重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 2. 数据转换</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, Tenant.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 3. 持久化</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异常处理规范" tabindex="-1">异常处理规范 <a class="header-anchor" href="#异常处理规范" aria-label="Permalink to &quot;异常处理规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 CheckedException.java - 框架统一异常</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 400 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户名称重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单 {0} 不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 404 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 403 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forbidden</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"登录过期,请重新登录"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 Optional 优雅处理</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span></code></pre>
</div><h2 id="学习路线" tabindex="-1">学习路线 <a class="header-anchor" href="#学习路线" aria-label="Permalink to &quot;学习路线&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>入门阶段（1-2周）</span></span>
<span class="line"><span>├─ 环境搭建</span></span>
<span class="line"><span>├─ 项目运行</span></span>
<span class="line"><span>└─ 基础 CRUD</span></span>
<span class="line"><span></span></span>
<span class="line"><span>进阶阶段（2-4周）</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>精通阶段（4周+）</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></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/advanced/performance.html">性能优化</a> - 学习性能优化技巧</li>
<li><a href="/zh/guide/advanced/best-practice.html">最佳实践</a> - 了解开发最佳实践</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[性能优化指南 ]]></title>
            <link>https://docs.battcn.com/zh/guide/advanced/performance.html</link>
            <guid>https://docs.battcn.com/zh/guide/advanced/performance.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="性能优化指南" tabindex="-1">性能优化指南 <a class="header-anchor" href="#性能优化指南" aria-label="Permalink to &quot;性能优化指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 性能优化的方法和技巧</p>
</div>
<h2 id="性能优化原则" tabindex="-1">性能优化原则 <a class="header-anchor" href="#性能优化原则" aria-label="Permalink to &quot;性能优化原则&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                    性能优化原则                          │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│  1. 先测量，再优化（不要盲目优化）                        │</span></span>
<span class="line"><span>│  2. 优化瓶颈点（二八法则）                               │</span></span>
<span class="line"><span>│  3. 空间换时间（缓存）                                   │</span></span>
<span class="line"><span>│  4. 异步化、并行化                                       │</span></span>
<span class="line"><span>│  5. 减少网络往返                                        │</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;">&ZeroWidthSpace;</a></h2>
<h3 id="索引优化" tabindex="-1">索引优化 <a class="header-anchor" href="#索引优化" aria-label="Permalink to &quot;索引优化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 1. 为常用查询条件添加索引</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_tenant_status</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(tenant_id, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_create_time</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(create_time);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 2. 覆盖索引（避免回表）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_cover</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(tenant_id, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, order_no, amount);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 3. 前缀索引（大字段）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_customer(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 4. 避免索引失效的情况</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 函数操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DATE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(create_time) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2024-01-01'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 范围查询</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> create_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2024-01-01'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> AND</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> create_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2024-01-02'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 隐式类型转换</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 123</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- order_no 是 varchar</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 类型匹配</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '123'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 前缀模糊查询</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIKE</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '%123'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 后缀模糊查询（可用索引）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIKE</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '123%'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><h3 id="慢查询优化" tabindex="-1">慢查询优化 <a class="header-anchor" href="#慢查询优化" aria-label="Permalink to &quot;慢查询优化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 开启慢查询日志</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> slow_query_log </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ON'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> long_query_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 分析执行计划</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">EXPLAIN </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> AND</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> status</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 优化建议</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 1. 避免 SELECT *</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id, order_no, amount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ...;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 2. 分页优化（深度分页）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 大偏移量</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIMIT</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 100000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 游标分页</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 100000</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ORDER BY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIMIT</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 3. 批量操作</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 循环插入</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Order order : orders) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    orderMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 批量插入</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">orderMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">insertBatch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orders);</span></span></code></pre>
</div><h3 id="分库分表" tabindex="-1">分库分表 <a class="header-anchor" href="#分库分表" aria-label="Permalink to &quot;分库分表&quot;">&ZeroWidthSpace;</a></h3>
<p>当单表数据量超过 1000W 时，考虑分库分表：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 ShardingSphere</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 按租户分库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.shardingsphere.rules.sharding.tables.t_order.actual</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nodes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ds_$</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{0..</span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}.t_order</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 按时间分表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.shardingsphere.rules.sharding.tables.t_order.table</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">strategy.standard.sharding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">column</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">create_time</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.shardingsphere.rules.sharding.tables.t_order.table</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">strategy.standard.sharding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">algorithm</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">t_order_inline</span></span></code></pre>
</div><h2 id="缓存优化" tabindex="-1">缓存优化 <a class="header-anchor" href="#缓存优化" aria-label="Permalink to &quot;缓存优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="多级缓存架构" tabindex="-1">多级缓存架构 <a class="header-anchor" href="#多级缓存架构" aria-label="Permalink to &quot;多级缓存架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐     ┌─────────────┐     ┌─────────────┐</span></span>
<span class="line"><span>│   本地缓存   │ ──▶ │  Redis 缓存  │ ──▶ │   数据库    │</span></span>
<span class="line"><span>│  (Caffeine) │     │             │     │   (MySQL)   │</span></span>
<span class="line"><span>└─────────────┘     └─────────────┘     └─────────────┘</span></span>
<span class="line"><span>     10μs              1-5ms              10-100ms</span></span></code></pre>
</div><h3 id="本地缓存" tabindex="-1">本地缓存 <a class="header-anchor" href="#本地缓存" aria-label="Permalink to &quot;本地缓存&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheConfig</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">localCache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Caffeine.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newBuilder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">maximumSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">expireAfterWrite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.MINUTES)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">recordStats</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DictService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> localCache;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StringRedisTemplate redisTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String key </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> code;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 1. 查本地缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Object cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getIfPresent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DictItem</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) cached;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 2. 查 Redis 缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String json </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isNotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(json)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> items </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(json, DictItem.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, items);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> items;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 3. 查数据库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> items </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(items), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.HOURS);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, items);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> items;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="缓存一致性" tabindex="-1">缓存一致性 <a class="header-anchor" href="#缓存一致性" aria-label="Permalink to &quot;缓存一致性&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 更新数据时删除缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 通知其他节点清除本地缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">convertAndSend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"cache:evict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"dict:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto.code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 监听缓存清除消息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheEvictListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> localCache;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostConstruct</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> subscribe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getConnectionFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getConnection</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">subscribe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((message, pattern) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                String key </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">invalidate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"cache:evict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBytes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="缓存穿透防护" tabindex="-1">缓存穿透防护 <a class="header-anchor" href="#缓存穿透防护" aria-label="Permalink to &quot;缓存穿透防护&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 1. 布隆过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BloomFilter&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIdFilter;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> User </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 先检查布隆过滤器</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userIdFilter.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mightContain</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 继续查询</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 2. 缓存空值</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> User </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String key </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "user:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"NULL"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">equals</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cached)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 缓存的空值</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cached, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 缓存空值，较短过期时间</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"NULL"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.MINUTES);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.HOURS);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> user;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="接口优化" tabindex="-1">接口优化 <a class="header-anchor" href="#接口优化" aria-label="Permalink to &quot;接口优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="批量处理" tabindex="-1">批量处理 <a class="header-anchor" href="#批量处理" aria-label="Permalink to &quot;批量处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 循环调用</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userIds) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 批量查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">listByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userIds);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userMap </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">collect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collectors.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, Function.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">identity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span></code></pre>
</div><h3 id="并行处理" tabindex="-1">并行处理 <a class="header-anchor" href="#并行处理" aria-label="Permalink to &quot;并行处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDetailService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderDetailVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getOrderDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDetailVO vo </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDetailVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 并行获取数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orderFuture </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supplyAsync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture&#x3C;List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>> itemsFuture </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supplyAsync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderItemMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByOrderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userFuture </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">thenCompose</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supplyAsync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            )</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 等待所有完成</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">allOf</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderFuture, itemsFuture, userFuture).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 组装结果</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setItems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(itemsFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> vo;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异步处理" tabindex="-1">异步处理 <a class="header-anchor" href="#异步处理" aria-label="Permalink to &quot;异步处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendOrderNotification</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送邮件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        emailService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送短信</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        smsService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 推送消息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        pushService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> doCreateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 异步发送通知，不阻塞主流程</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        sendOrderNotification</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="接口响应压缩" tabindex="-1">接口响应压缩 <a class="header-anchor" href="#接口响应压缩" aria-label="Permalink to &quot;接口响应压缩&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  compression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    mime-types</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">application/json,application/xml,text/html,text/plain</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    min-response-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1024</span></span></code></pre>
</div><h2 id="微服务配置优化" tabindex="-1">微服务配置优化 <a class="header-anchor" href="#微服务配置优化" aria-label="Permalink to &quot;微服务配置优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="优雅停机与虚拟线程" tabindex="-1">优雅停机与虚拟线程 <a class="header-anchor" href="#优雅停机与虚拟线程" aria-label="Permalink to &quot;优雅停机与虚拟线程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  shutdown</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">graceful</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                    # 开启优雅停机</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  threads</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    virtual</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                     # 开启全局虚拟线程（JDK 21+）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  reactor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    context-propagation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">auto</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">           # 上下文自动传播</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  lifecycle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    timeout-per-shutdown-phase</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">30s</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     # 给足时间处理旧请求</span></span></code></pre>
</div><h3 id="feign-与负载均衡配置" tabindex="-1">Feign 与负载均衡配置 <a class="header-anchor" href="#feign-与负载均衡配置" aria-label="Permalink to &quot;Feign 与负载均衡配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    openfeign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      httpclient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        hc5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                 # 使用 Apache HttpClient 5</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      client</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 全局配置</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            connect-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 连接超时 1s（K8s 内部超 1s 通常 IP 已死）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            read-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         # 读取超时 60s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    loadbalancer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                   # 使用 Nacos 负载均衡</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retry</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                   # 开启重试</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        max-retries-on-same-service-instance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 不在同一个坏 IP 上死磕</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        max-retries-on-next-service-instance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 切换到下一个实例重试 2 次</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        retryable-status-codes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">503, 500</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 针对哪些状态码重试</span></span></code></pre>
</div><h3 id="监控与链路追踪" tabindex="-1">监控与链路追踪 <a class="header-anchor" href="#监控与链路追踪" aria-label="Permalink to &quot;监控与链路追踪&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">management</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  tracing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    sampling</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      probability</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.0</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                  # 采样率 100%（生产可调低）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    propagation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">w3c, b3</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                     # 支持 W3C 和 B3 格式</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    baggage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      remote-fields</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">x-request-id</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      correlation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        fields</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">x-request-id</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  endpoints</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    web</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      exposure</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        include</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"health,info,prometheus"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 只暴露必要端点</span></span></code></pre>
</div><hr>
<h2 id="jvm-优化" tabindex="-1">JVM 优化 <a class="header-anchor" href="#jvm-优化" aria-label="Permalink to &quot;JVM 优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="内存配置" tabindex="-1">内存配置 <a class="header-anchor" href="#内存配置" aria-label="Permalink to &quot;内存配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 生产环境推荐配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">JAVA_OPTS</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-Xms2g -Xmx2g </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:+UseG1GC </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:MaxGCPauseMillis=200 </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:+HeapDumpOnOutOfMemoryError </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:HeapDumpPath=/app/logs/heapdump.hprof </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -Xlog:gc*:file=/app/logs/gc.log:time,uptime:filecount=5,filesize=10M"</span></span></code></pre>
</div><h3 id="gc-调优" tabindex="-1">GC 调优 <a class="header-anchor" href="#gc-调优" aria-label="Permalink to &quot;GC 调优&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># G1 收集器（推荐）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:MaxGCPauseMillis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=200</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:G1HeapRegionSize</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=16m</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 大堆内存场景</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:+UseZGC</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # JDK 17+</span></span></code></pre>
</div><h2 id="监控与诊断" tabindex="-1">监控与诊断 <a class="header-anchor" href="#监控与诊断" aria-label="Permalink to &quot;监控与诊断&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="接口性能监控" tabindex="-1">接口性能监控 <a class="header-anchor" href="#接口性能监控" aria-label="Permalink to &quot;接口性能监控&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Aspect</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PerformanceAspect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Around</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"@annotation(org.springframework.web.bind.annotation.RequestMapping)"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Object </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">around</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ProceedingJoinPoint </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">point</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throws</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Throwable {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> start </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String method </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> point.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toShortString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> point.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">proceed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cost </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> start;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (cost </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"慢接口: {} 耗时: {}ms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, method, cost);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"接口: {} 耗时: {}ms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, method, cost);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="性能指标采集" tabindex="-1">性能指标采集 <a class="header-anchor" href="#性能指标采集" aria-label="Permalink to &quot;性能指标采集&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Prometheus 指标</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">management</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  endpoints</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    web</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      exposure</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        include</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">prometheus,health,info</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  metrics</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    tags</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${spring.application.name}</span></span></code></pre>
</div><h2 id="性能检查清单" tabindex="-1">性能检查清单 <a class="header-anchor" href="#性能检查清单" aria-label="Permalink to &quot;性能检查清单&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>检查项</th>
<th>标准</th>
</tr>
</thead>
<tbody>
<tr>
<td>接口响应时间</td>
<td>P99 &lt; 500ms</td>
</tr>
<tr>
<td>数据库查询</td>
<td>单次 &lt; 100ms</td>
</tr>
<tr>
<td>缓存命中率</td>
<td>&gt; 90%</td>
</tr>
<tr>
<td>CPU 使用率</td>
<td>&lt; 70%</td>
</tr>
<tr>
<td>内存使用率</td>
<td>&lt; 80%</td>
</tr>
<tr>
<td>GC 停顿时间</td>
<td>&lt; 200ms</td>
</tr>
</tbody>
</table>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/advanced/best-practice.html">最佳实践</a> - 了解开发最佳实践</li>
<li><a href="/zh/guide/packages/devops/monitor.html">监控中心</a> - 配置监控系统</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="性能优化指南" tabindex="-1">性能优化指南 <a class="header-anchor" href="#性能优化指南" aria-label="Permalink to &quot;性能优化指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 性能优化的方法和技巧</p>
</div>
<h2 id="性能优化原则" tabindex="-1">性能优化原则 <a class="header-anchor" href="#性能优化原则" aria-label="Permalink to &quot;性能优化原则&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                    性能优化原则                          │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│  1. 先测量，再优化（不要盲目优化）                        │</span></span>
<span class="line"><span>│  2. 优化瓶颈点（二八法则）                               │</span></span>
<span class="line"><span>│  3. 空间换时间（缓存）                                   │</span></span>
<span class="line"><span>│  4. 异步化、并行化                                       │</span></span>
<span class="line"><span>│  5. 减少网络往返                                        │</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;">&ZeroWidthSpace;</a></h2>
<h3 id="索引优化" tabindex="-1">索引优化 <a class="header-anchor" href="#索引优化" aria-label="Permalink to &quot;索引优化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 1. 为常用查询条件添加索引</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_tenant_status</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(tenant_id, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_create_time</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(create_time);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 2. 覆盖索引（避免回表）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_cover</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(tenant_id, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, order_no, amount);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 3. 前缀索引（大字段）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_customer(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 4. 避免索引失效的情况</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 函数操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DATE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(create_time) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2024-01-01'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 范围查询</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> create_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2024-01-01'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> AND</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> create_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2024-01-02'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 隐式类型转换</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 123</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- order_no 是 varchar</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 类型匹配</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '123'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 前缀模糊查询</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIKE</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '%123'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 后缀模糊查询（可用索引）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order_no </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIKE</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '123%'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><h3 id="慢查询优化" tabindex="-1">慢查询优化 <a class="header-anchor" href="#慢查询优化" aria-label="Permalink to &quot;慢查询优化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 开启慢查询日志</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> slow_query_log </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ON'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SET</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> GLOBAL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> long_query_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 分析执行计划</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">EXPLAIN </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> AND</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> status</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 优化建议</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 1. 避免 SELECT *</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id, order_no, amount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ...;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 2. 分页优化（深度分页）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 大偏移量</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIMIT</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 100000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 游标分页</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 100000</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ORDER BY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LIMIT</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 3. 批量操作</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ❌ 循环插入</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Order order : orders) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    orderMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- ✅ 批量插入</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">orderMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">insertBatch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orders);</span></span></code></pre>
</div><h3 id="分库分表" tabindex="-1">分库分表 <a class="header-anchor" href="#分库分表" aria-label="Permalink to &quot;分库分表&quot;">&ZeroWidthSpace;</a></h3>
<p>当单表数据量超过 1000W 时，考虑分库分表：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 ShardingSphere</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 按租户分库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.shardingsphere.rules.sharding.tables.t_order.actual</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nodes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ds_$</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{0..</span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}.t_order</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 按时间分表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.shardingsphere.rules.sharding.tables.t_order.table</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">strategy.standard.sharding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">column</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">create_time</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.shardingsphere.rules.sharding.tables.t_order.table</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">strategy.standard.sharding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">algorithm</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">t_order_inline</span></span></code></pre>
</div><h2 id="缓存优化" tabindex="-1">缓存优化 <a class="header-anchor" href="#缓存优化" aria-label="Permalink to &quot;缓存优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="多级缓存架构" tabindex="-1">多级缓存架构 <a class="header-anchor" href="#多级缓存架构" aria-label="Permalink to &quot;多级缓存架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐     ┌─────────────┐     ┌─────────────┐</span></span>
<span class="line"><span>│   本地缓存   │ ──▶ │  Redis 缓存  │ ──▶ │   数据库    │</span></span>
<span class="line"><span>│  (Caffeine) │     │             │     │   (MySQL)   │</span></span>
<span class="line"><span>└─────────────┘     └─────────────┘     └─────────────┘</span></span>
<span class="line"><span>     10μs              1-5ms              10-100ms</span></span></code></pre>
</div><h3 id="本地缓存" tabindex="-1">本地缓存 <a class="header-anchor" href="#本地缓存" aria-label="Permalink to &quot;本地缓存&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheConfig</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">localCache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Caffeine.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newBuilder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">maximumSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">expireAfterWrite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.MINUTES)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">recordStats</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DictService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> localCache;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StringRedisTemplate redisTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String key </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> code;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 1. 查本地缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Object cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getIfPresent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DictItem</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) cached;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 2. 查 Redis 缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String json </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (StrUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isNotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(json)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> items </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(json, DictItem.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, items);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> items;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 3. 查数据库</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> items </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(items), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.HOURS);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, items);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> items;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="缓存一致性" tabindex="-1">缓存一致性 <a class="header-anchor" href="#缓存一致性" aria-label="Permalink to &quot;缓存一致性&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 更新数据时删除缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 通知其他节点清除本地缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">convertAndSend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"cache:evict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"dict:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto.code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 监听缓存清除消息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheEvictListener</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> localCache;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostConstruct</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> subscribe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getConnectionFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getConnection</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">subscribe</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((message, pattern) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                String key </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                localCache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">invalidate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"cache:evict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBytes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="缓存穿透防护" tabindex="-1">缓存穿透防护 <a class="header-anchor" href="#缓存穿透防护" aria-label="Permalink to &quot;缓存穿透防护&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 1. 布隆过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BloomFilter&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIdFilter;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> User </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 先检查布隆过滤器</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userIdFilter.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mightContain</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 继续查询</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 2. 缓存空值</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> User </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String key </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "user:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"NULL"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">equals</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cached)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 缓存的空值</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (cached </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cached, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 缓存空值，较短过期时间</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"NULL"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.MINUTES);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.HOURS);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> user;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="接口优化" tabindex="-1">接口优化 <a class="header-anchor" href="#接口优化" aria-label="Permalink to &quot;接口优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="批量处理" tabindex="-1">批量处理 <a class="header-anchor" href="#批量处理" aria-label="Permalink to &quot;批量处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ❌ 循环调用</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userIds) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// ✅ 批量查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">listByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userIds);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userMap </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">collect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Collectors.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, Function.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">identity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span></code></pre>
</div><h3 id="并行处理" tabindex="-1">并行处理 <a class="header-anchor" href="#并行处理" aria-label="Permalink to &quot;并行处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDetailService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderDetailVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getOrderDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderDetailVO vo </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderDetailVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 并行获取数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orderFuture </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supplyAsync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture&#x3C;List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>> itemsFuture </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supplyAsync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderItemMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByOrderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userFuture </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">thenCompose</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supplyAsync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            )</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 等待所有完成</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">allOf</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderFuture, itemsFuture, userFuture).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 组装结果</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setItems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(itemsFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">join</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> vo;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异步处理" tabindex="-1">异步处理 <a class="header-anchor" href="#异步处理" aria-label="Permalink to &quot;异步处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendOrderNotification</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送邮件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        emailService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送短信</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        smsService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 推送消息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        pushService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> doCreateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 异步发送通知，不阻塞主流程</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        sendOrderNotification</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> order;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="接口响应压缩" tabindex="-1">接口响应压缩 <a class="header-anchor" href="#接口响应压缩" aria-label="Permalink to &quot;接口响应压缩&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  compression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    mime-types</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">application/json,application/xml,text/html,text/plain</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    min-response-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1024</span></span></code></pre>
</div><h2 id="微服务配置优化" tabindex="-1">微服务配置优化 <a class="header-anchor" href="#微服务配置优化" aria-label="Permalink to &quot;微服务配置优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="优雅停机与虚拟线程" tabindex="-1">优雅停机与虚拟线程 <a class="header-anchor" href="#优雅停机与虚拟线程" aria-label="Permalink to &quot;优雅停机与虚拟线程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  shutdown</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">graceful</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                    # 开启优雅停机</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  threads</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    virtual</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                     # 开启全局虚拟线程（JDK 21+）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  reactor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    context-propagation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">auto</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">           # 上下文自动传播</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  lifecycle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    timeout-per-shutdown-phase</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">30s</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     # 给足时间处理旧请求</span></span></code></pre>
</div><h3 id="feign-与负载均衡配置" tabindex="-1">Feign 与负载均衡配置 <a class="header-anchor" href="#feign-与负载均衡配置" aria-label="Permalink to &quot;Feign 与负载均衡配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    openfeign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      httpclient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        hc5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                 # 使用 Apache HttpClient 5</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      client</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 全局配置</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            connect-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 连接超时 1s（K8s 内部超 1s 通常 IP 已死）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            read-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         # 读取超时 60s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    loadbalancer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                   # 使用 Nacos 负载均衡</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retry</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                   # 开启重试</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        max-retries-on-same-service-instance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 不在同一个坏 IP 上死磕</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        max-retries-on-next-service-instance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 切换到下一个实例重试 2 次</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        retryable-status-codes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">503, 500</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 针对哪些状态码重试</span></span></code></pre>
</div><h3 id="监控与链路追踪" tabindex="-1">监控与链路追踪 <a class="header-anchor" href="#监控与链路追踪" aria-label="Permalink to &quot;监控与链路追踪&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">management</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  tracing</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    sampling</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      probability</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.0</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                  # 采样率 100%（生产可调低）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    propagation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">w3c, b3</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                     # 支持 W3C 和 B3 格式</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    baggage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      remote-fields</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">x-request-id</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      correlation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        fields</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">x-request-id</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  endpoints</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    web</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      exposure</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        include</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"health,info,prometheus"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 只暴露必要端点</span></span></code></pre>
</div><hr>
<h2 id="jvm-优化" tabindex="-1">JVM 优化 <a class="header-anchor" href="#jvm-优化" aria-label="Permalink to &quot;JVM 优化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="内存配置" tabindex="-1">内存配置 <a class="header-anchor" href="#内存配置" aria-label="Permalink to &quot;内存配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 生产环境推荐配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">JAVA_OPTS</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-Xms2g -Xmx2g </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:+UseG1GC </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:MaxGCPauseMillis=200 </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:+HeapDumpOnOutOfMemoryError </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -XX:HeapDumpPath=/app/logs/heapdump.hprof </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  -Xlog:gc*:file=/app/logs/gc.log:time,uptime:filecount=5,filesize=10M"</span></span></code></pre>
</div><h3 id="gc-调优" tabindex="-1">GC 调优 <a class="header-anchor" href="#gc-调优" aria-label="Permalink to &quot;GC 调优&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># G1 收集器（推荐）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:MaxGCPauseMillis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=200</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:G1HeapRegionSize</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=16m</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 大堆内存场景</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">-XX:+UseZGC</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # JDK 17+</span></span></code></pre>
</div><h2 id="监控与诊断" tabindex="-1">监控与诊断 <a class="header-anchor" href="#监控与诊断" aria-label="Permalink to &quot;监控与诊断&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="接口性能监控" tabindex="-1">接口性能监控 <a class="header-anchor" href="#接口性能监控" aria-label="Permalink to &quot;接口性能监控&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Aspect</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PerformanceAspect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Around</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"@annotation(org.springframework.web.bind.annotation.RequestMapping)"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Object </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">around</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ProceedingJoinPoint </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">point</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throws</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Throwable {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> start </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String method </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> point.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getSignature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toShortString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> point.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">proceed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cost </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> start;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (cost </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"慢接口: {} 耗时: {}ms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, method, cost);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"接口: {} 耗时: {}ms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, method, cost);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="性能指标采集" tabindex="-1">性能指标采集 <a class="header-anchor" href="#性能指标采集" aria-label="Permalink to &quot;性能指标采集&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Prometheus 指标</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">management</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  endpoints</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    web</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      exposure</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        include</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">prometheus,health,info</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  metrics</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    tags</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${spring.application.name}</span></span></code></pre>
</div><h2 id="性能检查清单" tabindex="-1">性能检查清单 <a class="header-anchor" href="#性能检查清单" aria-label="Permalink to &quot;性能检查清单&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>检查项</th>
<th>标准</th>
</tr>
</thead>
<tbody>
<tr>
<td>接口响应时间</td>
<td>P99 &lt; 500ms</td>
</tr>
<tr>
<td>数据库查询</td>
<td>单次 &lt; 100ms</td>
</tr>
<tr>
<td>缓存命中率</td>
<td>&gt; 90%</td>
</tr>
<tr>
<td>CPU 使用率</td>
<td>&lt; 70%</td>
</tr>
<tr>
<td>内存使用率</td>
<td>&lt; 80%</td>
</tr>
<tr>
<td>GC 停顿时间</td>
<td>&lt; 200ms</td>
</tr>
</tbody>
</table>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/advanced/best-practice.html">最佳实践</a> - 了解开发最佳实践</li>
<li><a href="/zh/guide/packages/devops/monitor.html">监控中心</a> - 配置监控系统</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[API 开发指南 ]]></title>
            <link>https://docs.battcn.com/zh/guide/backend/api.html</link>
            <guid>https://docs.battcn.com/zh/guide/backend/api.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="api-开发指南" tabindex="-1">API 开发指南 <a class="header-anchor" href="#api-开发指南" aria-label="Permalink to &quot;API 开发指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 后端 API 开发的标准流程和最佳实践</p>
</div>
<h2 id="开发流程" tabindex="-1">开发流程 <a class="header-anchor" href="#开发流程" aria-label="Permalink to &quot;开发流程&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>设计接口 → 创建实体 → 编写 Mapper → 实现 Service → 编写 Controller → 测试验证</span></span></code></pre>
</div><h2 id="完整示例" tabindex="-1">完整示例 <a class="header-anchor" href="#完整示例" aria-label="Permalink to &quot;完整示例&quot;">&ZeroWidthSpace;</a></h2>
<p>以「文章管理」为例，演示完整的 CRUD 开发流程。</p>
<h3 id="_1-数据库设计" tabindex="-1">1. 数据库设计 <a class="header-anchor" href="#_1-数据库设计" aria-label="Permalink to &quot;1. 数据库设计&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> `</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">t_article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">` (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `id`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AUTO_INCREMENT COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'主键'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `title`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> varchar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NOT NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'标题'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `content`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'内容'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `author`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> varchar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'作者'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `status`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> tinyint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '0'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态：0-草稿 1-发布'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `view_count`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> int</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '0'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'浏览量'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `tenant_id`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `create_time`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> datetime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CURRENT_TIMESTAMP COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `update_time`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> datetime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CURRENT_TIMESTAMP </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ON</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> UPDATE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CURRENT_TIMESTAMP COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'更新时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `created_by`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `updated_by`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'更新人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`id`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    KEY</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> `idx_tenant_id`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`tenant_id`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) ENGINE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">InnoDB COMMENT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'文章表'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><h3 id="_2-创建实体类" tabindex="-1">2. 创建实体类 <a class="header-anchor" href="#_2-创建实体类" aria-label="Permalink to &quot;2. 创建实体类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.annotation.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.entity.SuperEntity;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.EqualsAndHashCode;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EqualsAndHashCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">callSuper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_article"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 标题 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 内容 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String content;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 作者 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 状态：0-草稿 1-发布 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 浏览量 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer viewCount;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 租户ID */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_3-创建-dto-vo" tabindex="-1">3. 创建 DTO/VO <a class="header-anchor" href="#_3-创建-dto-vo" aria-label="Permalink to &quot;3. 创建 DTO/VO&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// DTO - 数据传输对象（接收前端参数）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> jakarta.validation.constraints.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "标题不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "标题最多200个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "内容不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String content;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "作者最多50个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Min</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态值无效"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态值无效"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// VO - 视图对象（返回前端数据）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.time.LocalDateTime;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String content;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String statusName;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer viewCount;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LocalDateTime createTime;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String createdByName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// PageQuery - 分页查询参数</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.page.PageRequest;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.EqualsAndHashCode;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EqualsAndHashCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">callSuper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticlePageQuery</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 标题（模糊查询） */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 状态 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 作者 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_4-创建-mapper" tabindex="-1">4. 创建 Mapper <a class="header-anchor" href="#_4-创建-mapper" aria-label="Permalink to &quot;4. 创建 Mapper&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.mapper;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.mybatisplus.ext.SuperMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity.Article;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.apache.ibatis.annotations.Mapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.apache.ibatis.annotations.Param;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleMapper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 分页查询文章列表</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectArticlePage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- mapper/ArticleMapper.xml --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;?</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">xml</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"1.0"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> encoding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"UTF-8"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">?></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;!</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DOCTYPE</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mapper</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> namespace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"com.wemirr.demo.mapper.ArticleMapper"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">select</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"selectArticlePage"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> resultType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"com.wemirr.demo.vo.ArticleVO"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        SELECT </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.id,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.title,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.content,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.author,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.status,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            CASE a.status WHEN 0 THEN '草稿' WHEN 1 THEN '已发布' END AS status_name,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.view_count,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.create_time,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            u.nick_name AS created_by_name</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        FROM t_article a</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        LEFT JOIN sys_user u ON a.created_by = u.id</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query.title != null and query.title != ''"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                AND a.title LIKE CONCAT('%', #{query.title}, '%')</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query.status != null"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                AND a.status = #{query.status}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query.author != null and query.author != ''"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                AND a.author LIKE CONCAT('%', #{query.author}, '%')</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ORDER BY a.create_time DESC</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">select</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="_5-创建-service" tabindex="-1">5. 创建 Service <a class="header-anchor" href="#_5-创建-service" aria-label="Permalink to &quot;5. 创建 Service&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Service 接口</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.mybatisplus.ext.SuperService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity.Article;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticleDTO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleService</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 分页查询</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 获取详情</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ArticleVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 创建文章</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 更新文章</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 发布文章</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Service 实现</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service.impl;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cn.hutool.core.bean.BeanUtil;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.mybatisplus.ext.SuperServiceImpl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.commons.exception.CheckedException;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity.Article;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticleDTO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.mapper.ArticleMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service.ArticleService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.RequiredArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.stereotype.Service;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.transaction.annotation.Transactional;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectArticlePage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), query);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Article article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"文章不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 增加浏览量</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        lambdaUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, id)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"view_count = view_count + 1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(article, ArticleVO.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Article article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, Article.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        article.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setViewCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (article.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            article.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 默认草稿</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        save</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(article);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Article article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"文章不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, article);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(article);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> updated </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lambdaUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, id)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getStatus, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">updated) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"发布失败"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_6-创建-controller" tabindex="-1">6. 创建 Controller <a class="header-anchor" href="#_6-创建-controller" aria-label="Permalink to &quot;6. 创建 Controller&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.controller;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.commons.entity.Result;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.log.annotation.SysLog;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticleDTO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service.ArticleService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> io.swagger.v3.oas.annotations.Operation;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> io.swagger.v3.oas.annotations.tags.Tag;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> jakarta.validation.Valid;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.RequiredArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cn.dev33.satoken.annotation.SaCheckPermission;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.web.bind.annotation.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "文章管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/articles"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleService articleService;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "分页查询"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:list"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "获取详情"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:view"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "新增文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "新增文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:edit"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "删除文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:delete"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "删除文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">removeById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "发布文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/publish"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:publish"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "发布文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="常用注解" tabindex="-1">常用注解 <a class="header-anchor" href="#常用注解" aria-label="Permalink to &quot;常用注解&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="校验注解" tabindex="-1">校验注解 <a class="header-anchor" href="#校验注解" aria-label="Permalink to &quot;校验注解&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>注解</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>@NotNull</code></td>
<td>不能为 null</td>
</tr>
<tr>
<td><code>@NotBlank</code></td>
<td>不能为空白字符串</td>
</tr>
<tr>
<td><code>@NotEmpty</code></td>
<td>集合不能为空</td>
</tr>
<tr>
<td><code>@Size</code></td>
<td>长度/大小范围</td>
</tr>
<tr>
<td><code>@Min</code> / <code>@Max</code></td>
<td>最小/最大值</td>
</tr>
<tr>
<td><code>@Pattern</code></td>
<td>正则匹配</td>
</tr>
<tr>
<td><code>@Email</code></td>
<td>邮箱格式</td>
</tr>
</tbody>
</table>
<h3 id="权限注解" tabindex="-1">权限注解 <a class="header-anchor" href="#权限注解" aria-label="Permalink to &quot;权限注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Sa-Token 权限注解</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 需要指定权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckRole</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"admin"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)                   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 需要指定角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckLogin</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                           // 需要登录</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组合权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:edit"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">mode</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SaMode.OR)</span></span></code></pre>
</div><h3 id="日志注解" tabindex="-1">日志注解 <a class="header-anchor" href="#日志注解" aria-label="Permalink to &quot;日志注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "操作描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LogType.OPERATION)</span></span></code></pre>
</div><h3 id="swagger-注解" tabindex="-1">Swagger 注解 <a class="header-anchor" href="#swagger-注解" aria-label="Permalink to &quot;Swagger 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "模块名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "接口描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "参数描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "字段描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
</div><h2 id="api-文档" tabindex="-1">API 文档 <a class="header-anchor" href="#api-文档" aria-label="Permalink to &quot;API 文档&quot;">&ZeroWidthSpace;</a></h2>
<p>项目集成了 SpringDoc（Swagger 3），启动后访问：</p>
<ul>
<li><strong>Swagger UI</strong>: <a href="http://localhost:5001/swagger-ui.html" target="_blank" rel="noreferrer">http://localhost:5001/swagger-ui.html</a></li>
<li><strong>API 文档</strong>: <a href="http://localhost:5001/v3/api-docs" target="_blank" rel="noreferrer">http://localhost:5001/v3/api-docs</a></li>
</ul>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/backend/service.html">服务开发</a> - 学习业务服务开发</li>
<li><a href="/zh/guide/saas/">多租户</a> - 了解 SaaS 多租户开发</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="api-开发指南" tabindex="-1">API 开发指南 <a class="header-anchor" href="#api-开发指南" aria-label="Permalink to &quot;API 开发指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 后端 API 开发的标准流程和最佳实践</p>
</div>
<h2 id="开发流程" tabindex="-1">开发流程 <a class="header-anchor" href="#开发流程" aria-label="Permalink to &quot;开发流程&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>设计接口 → 创建实体 → 编写 Mapper → 实现 Service → 编写 Controller → 测试验证</span></span></code></pre>
</div><h2 id="完整示例" tabindex="-1">完整示例 <a class="header-anchor" href="#完整示例" aria-label="Permalink to &quot;完整示例&quot;">&ZeroWidthSpace;</a></h2>
<p>以「文章管理」为例，演示完整的 CRUD 开发流程。</p>
<h3 id="_1-数据库设计" tabindex="-1">1. 数据库设计 <a class="header-anchor" href="#_1-数据库设计" aria-label="Permalink to &quot;1. 数据库设计&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> `</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">t_article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">` (</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `id`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AUTO_INCREMENT COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'主键'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `title`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> varchar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NOT NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'标题'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `content`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'内容'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `author`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> varchar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'作者'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `status`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> tinyint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '0'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态：0-草稿 1-发布'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `view_count`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> int</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '0'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'浏览量'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `tenant_id`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `create_time`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> datetime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CURRENT_TIMESTAMP COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `update_time`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> datetime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CURRENT_TIMESTAMP </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ON</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> UPDATE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CURRENT_TIMESTAMP COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'更新时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `created_by`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    `updated_by`</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NULL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'更新人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`id`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    KEY</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> `idx_tenant_id`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`tenant_id`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) ENGINE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">InnoDB COMMENT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'文章表'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><h3 id="_2-创建实体类" tabindex="-1">2. 创建实体类 <a class="header-anchor" href="#_2-创建实体类" aria-label="Permalink to &quot;2. 创建实体类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.annotation.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.entity.SuperEntity;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.EqualsAndHashCode;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EqualsAndHashCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">callSuper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_article"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 标题 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 内容 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String content;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 作者 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 状态：0-草稿 1-发布 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 浏览量 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer viewCount;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 租户ID */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_3-创建-dto-vo" tabindex="-1">3. 创建 DTO/VO <a class="header-anchor" href="#_3-创建-dto-vo" aria-label="Permalink to &quot;3. 创建 DTO/VO&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// DTO - 数据传输对象（接收前端参数）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> jakarta.validation.constraints.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "标题不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "标题最多200个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "内容不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String content;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "作者最多50个字符"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Min</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态值无效"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态值无效"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// VO - 视图对象（返回前端数据）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.time.LocalDateTime;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String content;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String statusName;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer viewCount;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LocalDateTime createTime;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String createdByName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// PageQuery - 分页查询参数</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.page.PageRequest;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.EqualsAndHashCode;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EqualsAndHashCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">callSuper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticlePageQuery</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 标题（模糊查询） */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String title;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 状态 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /** 作者 */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String author;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_4-创建-mapper" tabindex="-1">4. 创建 Mapper <a class="header-anchor" href="#_4-创建-mapper" aria-label="Permalink to &quot;4. 创建 Mapper&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.mapper;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.mybatisplus.ext.SuperMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity.Article;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.apache.ibatis.annotations.Mapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.apache.ibatis.annotations.Param;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleMapper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 分页查询文章列表</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectArticlePage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- mapper/ArticleMapper.xml --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;?</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">xml</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"1.0"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> encoding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"UTF-8"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">?></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;!</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DOCTYPE</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mapper</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> namespace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"com.wemirr.demo.mapper.ArticleMapper"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">select</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"selectArticlePage"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> resultType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"com.wemirr.demo.vo.ArticleVO"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        SELECT </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.id,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.title,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.content,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.author,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.status,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            CASE a.status WHEN 0 THEN '草稿' WHEN 1 THEN '已发布' END AS status_name,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.view_count,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            a.create_time,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            u.nick_name AS created_by_name</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        FROM t_article a</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        LEFT JOIN sys_user u ON a.created_by = u.id</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query.title != null and query.title != ''"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                AND a.title LIKE CONCAT('%', #{query.title}, '%')</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query.status != null"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                AND a.status = #{query.status}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"query.author != null and query.author != ''"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                AND a.author LIKE CONCAT('%', #{query.author}, '%')</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ORDER BY a.create_time DESC</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">select</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="_5-创建-service" tabindex="-1">5. 创建 Service <a class="header-anchor" href="#_5-创建-service" aria-label="Permalink to &quot;5. 创建 Service&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Service 接口</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.mybatisplus.ext.SuperService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity.Article;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticleDTO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleService</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 分页查询</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 获取详情</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ArticleVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 创建文章</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 更新文章</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 发布文章</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Service 实现</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service.impl;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cn.hutool.core.bean.BeanUtil;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.mybatisplus.ext.SuperServiceImpl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.commons.exception.CheckedException;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.entity.Article;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticleDTO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.mapper.ArticleMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service.ArticleService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.RequiredArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.stereotype.Service;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.transaction.annotation.Transactional;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Article</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectArticlePage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), query);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Article article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"文章不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 增加浏览量</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        lambdaUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, id)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"view_count = view_count + 1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(article, ArticleVO.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Article article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, Article.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        article.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setViewCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (article.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            article.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 默认草稿</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        save</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(article);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Article article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (article </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"文章不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, article);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(article);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> updated </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lambdaUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId, id)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Article</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getStatus, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">updated) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"发布失败"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_6-创建-controller" tabindex="-1">6. 创建 Controller <a class="header-anchor" href="#_6-创建-controller" aria-label="Permalink to &quot;6. 创建 Controller&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.controller;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.baomidou.mybatisplus.core.metadata.IPage;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.commons.entity.Result;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.log.annotation.SysLog;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticleDTO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.dto.ArticlePageQuery;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.service.ArticleService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.demo.vo.ArticleVO;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> io.swagger.v3.oas.annotations.Operation;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> io.swagger.v3.oas.annotations.tags.Tag;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> jakarta.validation.Valid;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.RequiredArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cn.dev33.satoken.annotation.SaCheckPermission;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.web.bind.annotation.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "文章管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/articles"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ArticleController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleService articleService;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "分页查询"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:list"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ArticlePageQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "获取详情"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:view"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ArticleVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "新增文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "新增文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:edit"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ArticleDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "删除文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:delete"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "删除文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">removeById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "发布文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/publish"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:publish"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "发布文章"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        articleService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">publish</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="常用注解" tabindex="-1">常用注解 <a class="header-anchor" href="#常用注解" aria-label="Permalink to &quot;常用注解&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="校验注解" tabindex="-1">校验注解 <a class="header-anchor" href="#校验注解" aria-label="Permalink to &quot;校验注解&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>注解</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>@NotNull</code></td>
<td>不能为 null</td>
</tr>
<tr>
<td><code>@NotBlank</code></td>
<td>不能为空白字符串</td>
</tr>
<tr>
<td><code>@NotEmpty</code></td>
<td>集合不能为空</td>
</tr>
<tr>
<td><code>@Size</code></td>
<td>长度/大小范围</td>
</tr>
<tr>
<td><code>@Min</code> / <code>@Max</code></td>
<td>最小/最大值</td>
</tr>
<tr>
<td><code>@Pattern</code></td>
<td>正则匹配</td>
</tr>
<tr>
<td><code>@Email</code></td>
<td>邮箱格式</td>
</tr>
</tbody>
</table>
<h3 id="权限注解" tabindex="-1">权限注解 <a class="header-anchor" href="#权限注解" aria-label="Permalink to &quot;权限注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Sa-Token 权限注解</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 需要指定权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckRole</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"admin"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)                   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 需要指定角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckLogin</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                           // 需要登录</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组合权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"article:edit"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">mode</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SaMode.OR)</span></span></code></pre>
</div><h3 id="日志注解" tabindex="-1">日志注解 <a class="header-anchor" href="#日志注解" aria-label="Permalink to &quot;日志注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "操作描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LogType.OPERATION)</span></span></code></pre>
</div><h3 id="swagger-注解" tabindex="-1">Swagger 注解 <a class="header-anchor" href="#swagger-注解" aria-label="Permalink to &quot;Swagger 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "模块名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "接口描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "参数描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "字段描述"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
</div><h2 id="api-文档" tabindex="-1">API 文档 <a class="header-anchor" href="#api-文档" aria-label="Permalink to &quot;API 文档&quot;">&ZeroWidthSpace;</a></h2>
<p>项目集成了 SpringDoc（Swagger 3），启动后访问：</p>
<ul>
<li><strong>Swagger UI</strong>: <a href="http://localhost:5001/swagger-ui.html" target="_blank" rel="noreferrer">http://localhost:5001/swagger-ui.html</a></li>
<li><strong>API 文档</strong>: <a href="http://localhost:5001/v3/api-docs" target="_blank" rel="noreferrer">http://localhost:5001/v3/api-docs</a></li>
</ul>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/backend/service.html">服务开发</a> - 学习业务服务开发</li>
<li><a href="/zh/guide/saas/">多租户</a> - 了解 SaaS 多租户开发</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[后端开发概述 ]]></title>
            <link>https://docs.battcn.com/zh/guide/backend/</link>
            <guid>https://docs.battcn.com/zh/guide/backend/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="后端开发概述" tabindex="-1">后端开发概述 <a class="header-anchor" href="#后端开发概述" aria-label="Permalink to &quot;后端开发概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 后端项目结构、开发规范和核心功能</p>
</div>
<h2 id="技术栈" tabindex="-1">技术栈 <a class="header-anchor" href="#技术栈" aria-label="Permalink to &quot;技术栈&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK</td>
<td>17/21</td>
<td>Java 开发工具包</td>
</tr>
<tr>
<td>Spring Boot</td>
<td>3.x</td>
<td>基础框架</td>
</tr>
<tr>
<td>Spring Cloud</td>
<td>2024.x</td>
<td>微服务框架</td>
</tr>
<tr>
<td>Spring Cloud Alibaba</td>
<td>2023.x</td>
<td>阿里微服务组件</td>
</tr>
<tr>
<td>Sa-Token</td>
<td>最新版</td>
<td>轻量级权限认证框架</td>
</tr>
<tr>
<td>MyBatis-Plus</td>
<td>3.5.x</td>
<td>增强版 MyBatis</td>
</tr>
<tr>
<td>Nacos</td>
<td>2.x</td>
<td>服务注册与配置中心</td>
</tr>
<tr>
<td>Redis</td>
<td>6+</td>
<td>缓存、分布式锁</td>
</tr>
<tr>
<td>MySQL</td>
<td>8.0</td>
<td>关系型数据库</td>
</tr>
</tbody>
</table>
<h2 id="项目结构" tabindex="-1">项目结构 <a class="header-anchor" href="#项目结构" aria-label="Permalink to &quot;项目结构&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform/</span></span>
<span class="line"><span>├── wemirr-platform-dependencies/       # 📦 依赖版本管理（BOM）</span></span>
<span class="line"><span>├── wemirr-platform-framework/          # 🔧 框架核心层</span></span>
<span class="line"><span>│   ├── common-framework-core/          # 基础核心（注解、接口、工具类）</span></span>
<span class="line"><span>│   ├── common-spring-boot-starter/     # Spring Boot 核心启动器</span></span>
<span class="line"><span>│   ├── db-spring-boot-starter/         # 数据库增强（MyBatis-Plus、多租户）</span></span>
<span class="line"><span>│   ├── redis-plus-spring-boot-starter/ # Redis 缓存增强</span></span>
<span class="line"><span>│   ├── security-spring-boot-starter/   # 安全认证（Sa-Token）</span></span>
<span class="line"><span>│   ├── feign-plugin-spring-boot-starter/ # Feign 增强</span></span>
<span class="line"><span>│   ├── easyexcel-spring-boot-starter/  # Excel 导入导出</span></span>
<span class="line"><span>│   ├── diff-log-spring-boot-starter/   # 差异日志</span></span>
<span class="line"><span>│   ├── i18n-spring-boot-starter/       # 国际化</span></span>
<span class="line"><span>│   ├── pdf-spring-boot-starter/        # PDF 生成</span></span>
<span class="line"><span>│   ├── robot-spring-boot-starter/      # 消息机器人</span></span>
<span class="line"><span>│   └── websocket-spring-boot-starter/  # 分布式 WebSocket</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-feign/              # 🔗 Feign 接口定义</span></span>
<span class="line"><span>│   ├── wemirr-platform-iam-api/        # IAM 服务 API</span></span>
<span class="line"><span>│   ├── wemirr-platform-suite-api/      # Suite 服务 API</span></span>
<span class="line"><span>│   └── wemirr-platform-workflow-api/   # 工作流服务 API</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-iam/                # 🔐 IAM 认证授权中心</span></span>
<span class="line"><span>│   └── src/main/java/.../iam/</span></span>
<span class="line"><span>│       ├── auth/                       # 认证模块</span></span>
<span class="line"><span>│       ├── base/                       # 基础模块</span></span>
<span class="line"><span>│       ├── system/                     # 系统管理</span></span>
<span class="line"><span>│       └── tenant/                     # 租户管理</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-suite/              # 💼 业务套件中心</span></span>
<span class="line"><span>├── wemirr-platform-gateway/            # 🚪 API 网关</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-plugin/                      # 🔌 插件扩展中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-ai/             # AI 能力（Langchain4j）</span></span>
<span class="line"><span>│   ├── wemirr-platform-workflow/       # 审批流程（Warm-Flow）</span></span>
<span class="line"><span>│   ├── wemirr-platform-monitor/        # 监控中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-tms/            # 运输管理</span></span>
<span class="line"><span>│   └── wemirr-platform-wms/            # 仓储管理</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>└── 附件/                                # 📁 配套资源</span></span>
<span class="line"><span>    ├── docker/                         # Docker 编排</span></span>
<span class="line"><span>    ├── mysql/                          # 数据库脚本</span></span>
<span class="line"><span>    ├── nacos/                          # Nacos 配置</span></span>
<span class="line"><span>    └── nginx/                          # Nginx 配置</span></span></code></pre>
</div><h2 id="模块说明" tabindex="-1">模块说明 <a class="header-anchor" href="#模块说明" aria-label="Permalink to &quot;模块说明&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="iam-服务结构" tabindex="-1">IAM 服务结构 <a class="header-anchor" href="#iam-服务结构" aria-label="Permalink to &quot;IAM 服务结构&quot;">&ZeroWidthSpace;</a></h3>
<p>以 <code>wemirr-platform-iam</code> 为例，展示服务内部结构：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform-iam/</span></span>
<span class="line"><span>└── src/main/java/com/wemirr/platform/iam/</span></span>
<span class="line"><span>    ├── IamApplication.java         # 启动类</span></span>
<span class="line"><span>    ├── auth/                       # 认证模块</span></span>
<span class="line"><span>    │   ├── controller/             # 登录、Token 等接口</span></span>
<span class="line"><span>    │   └── service/</span></span>
<span class="line"><span>    ├── base/                       # 基础模块</span></span>
<span class="line"><span>    ├── system/                     # 系统管理</span></span>
<span class="line"><span>    │   ├── controller/             # 用户、角色、菜单等接口</span></span>
<span class="line"><span>    │   │   ├── UserController.java</span></span>
<span class="line"><span>    │   │   ├── RoleController.java</span></span>
<span class="line"><span>    │   │   ├── ResourceController.java</span></span>
<span class="line"><span>    │   │   ├── OrgController.java</span></span>
<span class="line"><span>    │   │   └── PositionController.java</span></span>
<span class="line"><span>    │   ├── domain/                 # 领域模型</span></span>
<span class="line"><span>    │   │   ├── entity/             # 实体类</span></span>
<span class="line"><span>    │   │   ├── dto/                # 请求对象</span></span>
<span class="line"><span>    │   │   │   ├── req/            # 请求参数</span></span>
<span class="line"><span>    │   │   │   └── resp/           # 响应结果</span></span>
<span class="line"><span>    │   │   └── convert/            # 对象转换</span></span>
<span class="line"><span>    │   ├── repository/             # 数据访问层（Mapper）</span></span>
<span class="line"><span>    │   ├── service/                # 业务逻辑</span></span>
<span class="line"><span>    │   └── func/                   # 功能函数</span></span>
<span class="line"><span>    └── tenant/                     # 租户管理</span></span></code></pre>
</div><h3 id="框架组件说明" tabindex="-1">框架组件说明 <a class="header-anchor" href="#框架组件说明" aria-label="Permalink to &quot;框架组件说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>common-framework-core</code></td>
<td>基础注解、接口、异常、工具类</td>
</tr>
<tr>
<td><code>common-spring-boot-starter</code></td>
<td>全局异常处理、参数校验、日志等</td>
</tr>
<tr>
<td><code>db-spring-boot-starter</code></td>
<td>MyBatis-Plus 配置、多租户、数据权限</td>
</tr>
<tr>
<td><code>redis-plus-spring-boot-starter</code></td>
<td>Redis 配置、分布式锁、缓存注解</td>
</tr>
<tr>
<td><code>security-spring-boot-starter</code></td>
<td>Sa-Token 配置、权限注解</td>
</tr>
<tr>
<td><code>feign-plugin-spring-boot-starter</code></td>
<td>Feign 增强、Token 传递、请求头复制</td>
</tr>
<tr>
<td><code>easyexcel-spring-boot-starter</code></td>
<td>Excel 导入导出，支持注解方式</td>
</tr>
<tr>
<td><code>diff-log-spring-boot-starter</code></td>
<td>变更日志记录</td>
</tr>
<tr>
<td><code>robot-spring-boot-starter</code></td>
<td>钉钉、企微、飞书机器人消息</td>
</tr>
<tr>
<td><code>websocket-spring-boot-starter</code></td>
<td>分布式 WebSocket 消息推送</td>
</tr>
</tbody>
</table>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-创建新服务" tabindex="-1">1. 创建新服务 <a class="header-anchor" href="#_1-创建新服务" aria-label="Permalink to &quot;1. 创建新服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 在 wemirr-platform 目录下创建新模块</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-demo/{demo-api,demo-biz,demo-server}</span></span></code></pre>
</div><h3 id="_2-配置-pom-xml" tabindex="-1">2. 配置 pom.xml <a class="header-anchor" href="#_2-配置-pom-xml" aria-label="Permalink to &quot;2. 配置 pom.xml&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- demo-server/pom.xml --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">parent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.platform&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>wemirr-platform-demo&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>${revision}&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">parent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>demo-server&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependencies</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.platform&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>demo-biz&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>common-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>db-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependencies</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="_3-创建启动类" tabindex="-1">3. 创建启动类 <a class="header-anchor" href="#_3-创建启动类" aria-label="Permalink to &quot;3. 创建启动类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SpringBootApplication</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EnableDiscoveryClient</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DemoApplication</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        SpringApplication.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DemoApplication.class, args);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_4-配置文件" tabindex="-1">4. 配置文件 <a class="header-anchor" href="#_4-配置文件" aria-label="Permalink to &quot;4. 配置文件&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># bootstrap.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-demo</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  profiles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    active</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">dev</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_SERVER_ADDR:localhost:8848}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        namespace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_NAMESPACE:v4-dev}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_SERVER_ADDR:localhost:8848}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        namespace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_NAMESPACE:v4-dev}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        file-extension</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        shared-configs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">data-id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">common.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            group</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">DEFAULT_GROUP</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            refresh</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="开发规范" tabindex="-1">开发规范 <a class="header-anchor" href="#开发规范" aria-label="Permalink to &quot;开发规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="代码分层" tabindex="-1">代码分层 <a class="header-anchor" href="#代码分层" aria-label="Permalink to &quot;代码分层&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Controller  →  接收请求、参数校验、调用 Service</span></span>
<span class="line"><span>    ↓</span></span>
<span class="line"><span>Service     →  业务逻辑、事务管理、调用 Mapper</span></span>
<span class="line"><span>    ↓</span></span>
<span class="line"><span>Mapper      →  数据访问、SQL 操作</span></span>
<span class="line"><span>    ↓</span></span>
<span class="line"><span>Entity      →  数据库实体映射</span></span></code></pre>
</div><h3 id="命名规范" tabindex="-1">命名规范 <a class="header-anchor" href="#命名规范" aria-label="Permalink to &quot;命名规范&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>规范</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>类名</td>
<td>PascalCase</td>
<td><code>UserController</code></td>
</tr>
<tr>
<td>方法名</td>
<td>camelCase</td>
<td><code>getUserById</code></td>
</tr>
<tr>
<td>变量名</td>
<td>camelCase</td>
<td><code>userName</code></td>
</tr>
<tr>
<td>常量</td>
<td>UPPER_SNAKE_CASE</td>
<td><code>MAX_RETRY_COUNT</code></td>
</tr>
<tr>
<td>包名</td>
<td>全小写</td>
<td><code>com.wemirr.iam</code></td>
</tr>
</tbody>
</table>
<h3 id="接口规范" tabindex="-1">接口规范 <a class="header-anchor" href="#接口规范" aria-label="Permalink to &quot;接口规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// RESTful 风格</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">GET    </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列表查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">GET    </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{id}     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 单个查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">POST   </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 新增</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">PUT    </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{id}     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 更新</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DELETE </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{id}     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 删除</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 统一响应格式</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "code"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 状态码，0 成功，其他失败</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "message"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "success"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 消息</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "data"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异常处理" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用统一业务异常</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 异常枚举</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ResultCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SUCCESS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    USER_NOT_FOUND</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10001</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    PARAM_ERROR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10002</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"参数错误"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="核心功能-真实代码示例" tabindex="-1">核心功能（真实代码示例） <a class="header-anchor" href="#核心功能-真实代码示例" aria-label="Permalink to &quot;核心功能（真实代码示例）&quot;">&ZeroWidthSpace;</a></h2>
<p>以下代码均来自 <code>wemirr-platform-iam</code> 模块，是项目中的真实实现。</p>
<h3 id="controller-示例" tabindex="-1">Controller 示例 <a class="header-anchor" href="#controller-示例" aria-label="Permalink to &quot;Controller 示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserService userService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/page"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:page"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/create"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编辑用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:edit"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserUpdateReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "删除用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:remove"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> del</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fileName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 一键导出 Excel</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">exportList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCurrent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRecords</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="service-示例" tabindex="-1">Service 示例 <a class="header-anchor" href="#service-示例" aria-label="Permalink to &quot;Service 示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 检查账号是否存在</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> count </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> super</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUsername, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (count </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"账号已存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        var</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> bean </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        bean.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PasswordEncoderHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">encode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        bean.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自动设置租户ID</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(bean);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">group</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编辑用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">businessKey</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "{{#id}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">            success</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息 {_DIFF{#_newObj}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">            fail</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息异常 {{#id}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, UserUpdateReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        User oldUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        User newUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtilPlus.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, req, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DiffLogContext.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">putDiffItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(oldUser, newUser);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 差异日志</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(newUser);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分布式事务</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getReadonly</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"内置用户不允许删除"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deleteById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userRoleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserRole</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserRole</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUserId, id));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="请求参数校验" tabindex="-1">请求参数校验 <a class="header-anchor" href="#请求参数校验" aria-label="Permalink to &quot;请求参数校验&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserSaveReq.java - 新增用户请求</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "UserSaveReq"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "保存用户信息实体"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserSaveReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号长度不能超过{max}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码长度不能超过{max}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String password;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "姓名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "姓名不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "姓名长度不能超过50"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">regexp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RegexPool.MOBILE, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号格式错误"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "性别不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sex sex;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserPageReq.java - 分页查询请求</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EqualsAndHashCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">callSuper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserPageReq</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "组织ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long orgId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="实体类定义" tabindex="-1">实体类定义 <a class="header-anchor" href="#实体类定义" aria-label="Permalink to &quot;实体类定义&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 User.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SuperBuilder</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NoArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_user"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "User"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">strategy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DiffFieldStrategy.NOT_NULL)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 差异对比字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "是否只读"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean readonly;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态(false=禁用;true=启用)"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "机构ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long orgId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异常处理-1" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理-1" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 框架提供的 CheckedException</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 400 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"账号已存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单 {0} 不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 404 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 403 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forbidden</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"登录过期,请重新登录"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h2 id="框架特有功能" tabindex="-1">框架特有功能 <a class="header-anchor" href="#框架特有功能" aria-label="Permalink to &quot;框架特有功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="常用注解速查" tabindex="-1">常用注解速查 <a class="header-anchor" href="#常用注解速查" aria-label="Permalink to &quot;常用注解速查&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>注解</th>
<th>所属模块</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>@SaCheckPermission</code></td>
<td>Sa-Token</td>
<td>权限校验</td>
</tr>
<tr>
<td><code>@AccessLog</code></td>
<td>common-framework</td>
<td>操作日志记录</td>
</tr>
<tr>
<td><code>@DiffLog</code></td>
<td>diff-log-starter</td>
<td>差异日志（自动记录修改前后对比）</td>
</tr>
<tr>
<td><code>@DiffField</code></td>
<td>diff-log-starter</td>
<td>标记需要对比的字段</td>
</tr>
<tr>
<td><code>@ResponseExcel</code></td>
<td>easyexcel-starter</td>
<td>一键导出 Excel</td>
</tr>
<tr>
<td><code>@TenantIgnore</code></td>
<td>db-starter</td>
<td>忽略租户过滤</td>
</tr>
<tr>
<td><code>@DSTransactional</code></td>
<td>db-starter</td>
<td>分布式事务</td>
</tr>
<tr>
<td><code>@RemoteResult</code></td>
<td>feign-starter</td>
<td>远程结果自动填充</td>
</tr>
</tbody>
</table>
<h3 id="常用工具类" tabindex="-1">常用工具类 <a class="header-anchor" href="#常用工具类" aria-label="Permalink to &quot;常用工具类&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AuthenticationContext</code></td>
<td>获取当前登录用户信息</td>
</tr>
<tr>
<td><code>CheckedException</code></td>
<td>统一业务异常</td>
</tr>
<tr>
<td><code>BeanUtilPlus</code></td>
<td>Bean 转换增强</td>
</tr>
<tr>
<td><code>Wraps</code></td>
<td>MyBatis-Plus 条件构造器简化</td>
</tr>
<tr>
<td><code>TenantHelper</code></td>
<td>租户切换辅助</td>
</tr>
<tr>
<td><code>PasswordEncoderHelper</code></td>
<td>密码加密</td>
</tr>
</tbody>
</table>
<h3 id="获取当前用户" tabindex="-1">获取当前用户 <a class="header-anchor" href="#获取当前用户" aria-label="Permalink to &quot;获取当前用户&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DemoService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> demo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 用户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 租户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 用户名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String nickname </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nickname</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 昵称</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">roles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 角色列表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="条件构造器简化" tabindex="-1">条件构造器简化 <a class="header-anchor" href="#条件构造器简化" aria-label="Permalink to &quot;条件构造器简化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 Wraps 简化 LambdaQueryWrapper 创建</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUsername, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">like</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getNickName, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getNickName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getStatus, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIds);</span></span></code></pre>
</div><h3 id="租户切换" tabindex="-1">租户切换 <a class="header-anchor" href="#租户切换" aria-label="Permalink to &quot;租户切换&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 临时忽略租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">withIgnoreStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换到指定租户执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8888"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><hr>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/backend/api.html">API 开发</a> - 学习 RESTful API 开发</li>
<li><a href="/zh/guide/backend/service.html">服务开发</a> - 学习业务服务开发</li>
<li><a href="/zh/guide/saas/">多租户</a> - 了解 SaaS 多租户开发</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="后端开发概述" tabindex="-1">后端开发概述 <a class="header-anchor" href="#后端开发概述" aria-label="Permalink to &quot;后端开发概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 后端项目结构、开发规范和核心功能</p>
</div>
<h2 id="技术栈" tabindex="-1">技术栈 <a class="header-anchor" href="#技术栈" aria-label="Permalink to &quot;技术栈&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK</td>
<td>17/21</td>
<td>Java 开发工具包</td>
</tr>
<tr>
<td>Spring Boot</td>
<td>3.x</td>
<td>基础框架</td>
</tr>
<tr>
<td>Spring Cloud</td>
<td>2024.x</td>
<td>微服务框架</td>
</tr>
<tr>
<td>Spring Cloud Alibaba</td>
<td>2023.x</td>
<td>阿里微服务组件</td>
</tr>
<tr>
<td>Sa-Token</td>
<td>最新版</td>
<td>轻量级权限认证框架</td>
</tr>
<tr>
<td>MyBatis-Plus</td>
<td>3.5.x</td>
<td>增强版 MyBatis</td>
</tr>
<tr>
<td>Nacos</td>
<td>2.x</td>
<td>服务注册与配置中心</td>
</tr>
<tr>
<td>Redis</td>
<td>6+</td>
<td>缓存、分布式锁</td>
</tr>
<tr>
<td>MySQL</td>
<td>8.0</td>
<td>关系型数据库</td>
</tr>
</tbody>
</table>
<h2 id="项目结构" tabindex="-1">项目结构 <a class="header-anchor" href="#项目结构" aria-label="Permalink to &quot;项目结构&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform/</span></span>
<span class="line"><span>├── wemirr-platform-dependencies/       # 📦 依赖版本管理（BOM）</span></span>
<span class="line"><span>├── wemirr-platform-framework/          # 🔧 框架核心层</span></span>
<span class="line"><span>│   ├── common-framework-core/          # 基础核心（注解、接口、工具类）</span></span>
<span class="line"><span>│   ├── common-spring-boot-starter/     # Spring Boot 核心启动器</span></span>
<span class="line"><span>│   ├── db-spring-boot-starter/         # 数据库增强（MyBatis-Plus、多租户）</span></span>
<span class="line"><span>│   ├── redis-plus-spring-boot-starter/ # Redis 缓存增强</span></span>
<span class="line"><span>│   ├── security-spring-boot-starter/   # 安全认证（Sa-Token）</span></span>
<span class="line"><span>│   ├── feign-plugin-spring-boot-starter/ # Feign 增强</span></span>
<span class="line"><span>│   ├── easyexcel-spring-boot-starter/  # Excel 导入导出</span></span>
<span class="line"><span>│   ├── diff-log-spring-boot-starter/   # 差异日志</span></span>
<span class="line"><span>│   ├── i18n-spring-boot-starter/       # 国际化</span></span>
<span class="line"><span>│   ├── pdf-spring-boot-starter/        # PDF 生成</span></span>
<span class="line"><span>│   ├── robot-spring-boot-starter/      # 消息机器人</span></span>
<span class="line"><span>│   └── websocket-spring-boot-starter/  # 分布式 WebSocket</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-feign/              # 🔗 Feign 接口定义</span></span>
<span class="line"><span>│   ├── wemirr-platform-iam-api/        # IAM 服务 API</span></span>
<span class="line"><span>│   ├── wemirr-platform-suite-api/      # Suite 服务 API</span></span>
<span class="line"><span>│   └── wemirr-platform-workflow-api/   # 工作流服务 API</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-iam/                # 🔐 IAM 认证授权中心</span></span>
<span class="line"><span>│   └── src/main/java/.../iam/</span></span>
<span class="line"><span>│       ├── auth/                       # 认证模块</span></span>
<span class="line"><span>│       ├── base/                       # 基础模块</span></span>
<span class="line"><span>│       ├── system/                     # 系统管理</span></span>
<span class="line"><span>│       └── tenant/                     # 租户管理</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-suite/              # 💼 业务套件中心</span></span>
<span class="line"><span>├── wemirr-platform-gateway/            # 🚪 API 网关</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-plugin/                      # 🔌 插件扩展中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-ai/             # AI 能力（Langchain4j）</span></span>
<span class="line"><span>│   ├── wemirr-platform-workflow/       # 审批流程（Warm-Flow）</span></span>
<span class="line"><span>│   ├── wemirr-platform-monitor/        # 监控中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-tms/            # 运输管理</span></span>
<span class="line"><span>│   └── wemirr-platform-wms/            # 仓储管理</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>└── 附件/                                # 📁 配套资源</span></span>
<span class="line"><span>    ├── docker/                         # Docker 编排</span></span>
<span class="line"><span>    ├── mysql/                          # 数据库脚本</span></span>
<span class="line"><span>    ├── nacos/                          # Nacos 配置</span></span>
<span class="line"><span>    └── nginx/                          # Nginx 配置</span></span></code></pre>
</div><h2 id="模块说明" tabindex="-1">模块说明 <a class="header-anchor" href="#模块说明" aria-label="Permalink to &quot;模块说明&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="iam-服务结构" tabindex="-1">IAM 服务结构 <a class="header-anchor" href="#iam-服务结构" aria-label="Permalink to &quot;IAM 服务结构&quot;">&ZeroWidthSpace;</a></h3>
<p>以 <code>wemirr-platform-iam</code> 为例，展示服务内部结构：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform-iam/</span></span>
<span class="line"><span>└── src/main/java/com/wemirr/platform/iam/</span></span>
<span class="line"><span>    ├── IamApplication.java         # 启动类</span></span>
<span class="line"><span>    ├── auth/                       # 认证模块</span></span>
<span class="line"><span>    │   ├── controller/             # 登录、Token 等接口</span></span>
<span class="line"><span>    │   └── service/</span></span>
<span class="line"><span>    ├── base/                       # 基础模块</span></span>
<span class="line"><span>    ├── system/                     # 系统管理</span></span>
<span class="line"><span>    │   ├── controller/             # 用户、角色、菜单等接口</span></span>
<span class="line"><span>    │   │   ├── UserController.java</span></span>
<span class="line"><span>    │   │   ├── RoleController.java</span></span>
<span class="line"><span>    │   │   ├── ResourceController.java</span></span>
<span class="line"><span>    │   │   ├── OrgController.java</span></span>
<span class="line"><span>    │   │   └── PositionController.java</span></span>
<span class="line"><span>    │   ├── domain/                 # 领域模型</span></span>
<span class="line"><span>    │   │   ├── entity/             # 实体类</span></span>
<span class="line"><span>    │   │   ├── dto/                # 请求对象</span></span>
<span class="line"><span>    │   │   │   ├── req/            # 请求参数</span></span>
<span class="line"><span>    │   │   │   └── resp/           # 响应结果</span></span>
<span class="line"><span>    │   │   └── convert/            # 对象转换</span></span>
<span class="line"><span>    │   ├── repository/             # 数据访问层（Mapper）</span></span>
<span class="line"><span>    │   ├── service/                # 业务逻辑</span></span>
<span class="line"><span>    │   └── func/                   # 功能函数</span></span>
<span class="line"><span>    └── tenant/                     # 租户管理</span></span></code></pre>
</div><h3 id="框架组件说明" tabindex="-1">框架组件说明 <a class="header-anchor" href="#框架组件说明" aria-label="Permalink to &quot;框架组件说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>common-framework-core</code></td>
<td>基础注解、接口、异常、工具类</td>
</tr>
<tr>
<td><code>common-spring-boot-starter</code></td>
<td>全局异常处理、参数校验、日志等</td>
</tr>
<tr>
<td><code>db-spring-boot-starter</code></td>
<td>MyBatis-Plus 配置、多租户、数据权限</td>
</tr>
<tr>
<td><code>redis-plus-spring-boot-starter</code></td>
<td>Redis 配置、分布式锁、缓存注解</td>
</tr>
<tr>
<td><code>security-spring-boot-starter</code></td>
<td>Sa-Token 配置、权限注解</td>
</tr>
<tr>
<td><code>feign-plugin-spring-boot-starter</code></td>
<td>Feign 增强、Token 传递、请求头复制</td>
</tr>
<tr>
<td><code>easyexcel-spring-boot-starter</code></td>
<td>Excel 导入导出，支持注解方式</td>
</tr>
<tr>
<td><code>diff-log-spring-boot-starter</code></td>
<td>变更日志记录</td>
</tr>
<tr>
<td><code>robot-spring-boot-starter</code></td>
<td>钉钉、企微、飞书机器人消息</td>
</tr>
<tr>
<td><code>websocket-spring-boot-starter</code></td>
<td>分布式 WebSocket 消息推送</td>
</tr>
</tbody>
</table>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-创建新服务" tabindex="-1">1. 创建新服务 <a class="header-anchor" href="#_1-创建新服务" aria-label="Permalink to &quot;1. 创建新服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 在 wemirr-platform 目录下创建新模块</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-demo/{demo-api,demo-biz,demo-server}</span></span></code></pre>
</div><h3 id="_2-配置-pom-xml" tabindex="-1">2. 配置 pom.xml <a class="header-anchor" href="#_2-配置-pom-xml" aria-label="Permalink to &quot;2. 配置 pom.xml&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- demo-server/pom.xml --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">parent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.platform&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>wemirr-platform-demo&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>${revision}&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">parent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>demo-server&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependencies</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.platform&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>demo-biz&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>common-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>db-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependencies</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="_3-创建启动类" tabindex="-1">3. 创建启动类 <a class="header-anchor" href="#_3-创建启动类" aria-label="Permalink to &quot;3. 创建启动类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SpringBootApplication</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EnableDiscoveryClient</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DemoApplication</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        SpringApplication.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DemoApplication.class, args);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_4-配置文件" tabindex="-1">4. 配置文件 <a class="header-anchor" href="#_4-配置文件" aria-label="Permalink to &quot;4. 配置文件&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># bootstrap.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-demo</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  profiles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    active</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">dev</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_SERVER_ADDR:localhost:8848}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        namespace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_NAMESPACE:v4-dev}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_SERVER_ADDR:localhost:8848}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        namespace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${NACOS_NAMESPACE:v4-dev}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        file-extension</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        shared-configs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">data-id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">common.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            group</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">DEFAULT_GROUP</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            refresh</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="开发规范" tabindex="-1">开发规范 <a class="header-anchor" href="#开发规范" aria-label="Permalink to &quot;开发规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="代码分层" tabindex="-1">代码分层 <a class="header-anchor" href="#代码分层" aria-label="Permalink to &quot;代码分层&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Controller  →  接收请求、参数校验、调用 Service</span></span>
<span class="line"><span>    ↓</span></span>
<span class="line"><span>Service     →  业务逻辑、事务管理、调用 Mapper</span></span>
<span class="line"><span>    ↓</span></span>
<span class="line"><span>Mapper      →  数据访问、SQL 操作</span></span>
<span class="line"><span>    ↓</span></span>
<span class="line"><span>Entity      →  数据库实体映射</span></span></code></pre>
</div><h3 id="命名规范" tabindex="-1">命名规范 <a class="header-anchor" href="#命名规范" aria-label="Permalink to &quot;命名规范&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>规范</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>类名</td>
<td>PascalCase</td>
<td><code>UserController</code></td>
</tr>
<tr>
<td>方法名</td>
<td>camelCase</td>
<td><code>getUserById</code></td>
</tr>
<tr>
<td>变量名</td>
<td>camelCase</td>
<td><code>userName</code></td>
</tr>
<tr>
<td>常量</td>
<td>UPPER_SNAKE_CASE</td>
<td><code>MAX_RETRY_COUNT</code></td>
</tr>
<tr>
<td>包名</td>
<td>全小写</td>
<td><code>com.wemirr.iam</code></td>
</tr>
</tbody>
</table>
<h3 id="接口规范" tabindex="-1">接口规范 <a class="header-anchor" href="#接口规范" aria-label="Permalink to &quot;接口规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// RESTful 风格</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">GET    </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列表查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">GET    </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{id}     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 单个查询</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">POST   </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 新增</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">PUT    </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{id}     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 更新</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DELETE </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{id}     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 删除</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 统一响应格式</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "code"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 状态码，0 成功，其他失败</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "message"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "success"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 消息</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "data"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异常处理" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用统一业务异常</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 异常枚举</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ResultCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SUCCESS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    USER_NOT_FOUND</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10001</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    PARAM_ERROR</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10002</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"参数错误"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="核心功能-真实代码示例" tabindex="-1">核心功能（真实代码示例） <a class="header-anchor" href="#核心功能-真实代码示例" aria-label="Permalink to &quot;核心功能（真实代码示例）&quot;">&ZeroWidthSpace;</a></h2>
<p>以下代码均来自 <code>wemirr-platform-iam</code> 模块，是项目中的真实实现。</p>
<h3 id="controller-示例" tabindex="-1">Controller 示例 <a class="header-anchor" href="#controller-示例" aria-label="Permalink to &quot;Controller 示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserService userService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/page"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:page"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/create"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编辑用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:edit"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserUpdateReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "删除用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:remove"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> del</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fileName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 一键导出 Excel</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserPageResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">exportList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCurrent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRecords</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="service-示例" tabindex="-1">Service 示例 <a class="header-anchor" href="#service-示例" aria-label="Permalink to &quot;Service 示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 检查账号是否存在</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> count </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> super</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUsername, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (count </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"账号已存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        var</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> bean </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        bean.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PasswordEncoderHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">encode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        bean.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自动设置租户ID</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(bean);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">group</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编辑用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">businessKey</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "{{#id}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">            success</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息 {_DIFF{#_newObj}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">            fail</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "更新用户信息异常 {{#id}}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, UserUpdateReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        User oldUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        User newUser </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtilPlus.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id, req, User.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DiffLogContext.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">putDiffItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(oldUser, newUser);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 差异日志</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(newUser);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分布式事务</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getReadonly</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"内置用户不允许删除"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deleteById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        userRoleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserRole</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserRole</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUserId, id));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="请求参数校验" tabindex="-1">请求参数校验 <a class="header-anchor" href="#请求参数校验" aria-label="Permalink to &quot;请求参数校验&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserSaveReq.java - 新增用户请求</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "UserSaveReq"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "保存用户信息实体"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserSaveReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号长度不能超过{max}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "密码长度不能超过{max}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String password;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "姓名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "姓名不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "姓名长度不能超过50"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">regexp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RegexPool.MOBILE, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号格式错误"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "性别不能为空"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Sex sex;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 UserPageReq.java - 分页查询请求</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EqualsAndHashCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">callSuper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserPageReq</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "组织ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long orgId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="实体类定义" tabindex="-1">实体类定义 <a class="header-anchor" href="#实体类定义" aria-label="Permalink to &quot;实体类定义&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 User.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SuperBuilder</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NoArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_user"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "User"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">strategy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DiffFieldStrategy.NOT_NULL)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 差异对比字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "昵称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String nickName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DiffField</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "是否只读"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean readonly;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态(false=禁用;true=启用)"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "机构ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long orgId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="异常处理-1" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理-1" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 框架提供的 CheckedException</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 400 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"账号已存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单 {0} 不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, orderId);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 404 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 403 错误</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forbidden</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"登录过期,请重新登录"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h2 id="框架特有功能" tabindex="-1">框架特有功能 <a class="header-anchor" href="#框架特有功能" aria-label="Permalink to &quot;框架特有功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="常用注解速查" tabindex="-1">常用注解速查 <a class="header-anchor" href="#常用注解速查" aria-label="Permalink to &quot;常用注解速查&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>注解</th>
<th>所属模块</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>@SaCheckPermission</code></td>
<td>Sa-Token</td>
<td>权限校验</td>
</tr>
<tr>
<td><code>@AccessLog</code></td>
<td>common-framework</td>
<td>操作日志记录</td>
</tr>
<tr>
<td><code>@DiffLog</code></td>
<td>diff-log-starter</td>
<td>差异日志（自动记录修改前后对比）</td>
</tr>
<tr>
<td><code>@DiffField</code></td>
<td>diff-log-starter</td>
<td>标记需要对比的字段</td>
</tr>
<tr>
<td><code>@ResponseExcel</code></td>
<td>easyexcel-starter</td>
<td>一键导出 Excel</td>
</tr>
<tr>
<td><code>@TenantIgnore</code></td>
<td>db-starter</td>
<td>忽略租户过滤</td>
</tr>
<tr>
<td><code>@DSTransactional</code></td>
<td>db-starter</td>
<td>分布式事务</td>
</tr>
<tr>
<td><code>@RemoteResult</code></td>
<td>feign-starter</td>
<td>远程结果自动填充</td>
</tr>
</tbody>
</table>
<h3 id="常用工具类" tabindex="-1">常用工具类 <a class="header-anchor" href="#常用工具类" aria-label="Permalink to &quot;常用工具类&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AuthenticationContext</code></td>
<td>获取当前登录用户信息</td>
</tr>
<tr>
<td><code>CheckedException</code></td>
<td>统一业务异常</td>
</tr>
<tr>
<td><code>BeanUtilPlus</code></td>
<td>Bean 转换增强</td>
</tr>
<tr>
<td><code>Wraps</code></td>
<td>MyBatis-Plus 条件构造器简化</td>
</tr>
<tr>
<td><code>TenantHelper</code></td>
<td>租户切换辅助</td>
</tr>
<tr>
<td><code>PasswordEncoderHelper</code></td>
<td>密码加密</td>
</tr>
</tbody>
</table>
<h3 id="获取当前用户" tabindex="-1">获取当前用户 <a class="header-anchor" href="#获取当前用户" aria-label="Permalink to &quot;获取当前用户&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DemoService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> demo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 用户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 租户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 用户名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String nickname </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nickname</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 昵称</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">roles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 角色列表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="条件构造器简化" tabindex="-1">条件构造器简化 <a class="header-anchor" href="#条件构造器简化" aria-label="Permalink to &quot;条件构造器简化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 Wraps 简化 LambdaQueryWrapper 创建</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getUsername, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">like</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getNickName, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getNickName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getStatus, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIds);</span></span></code></pre>
</div><h3 id="租户切换" tabindex="-1">租户切换 <a class="header-anchor" href="#租户切换" aria-label="Permalink to &quot;租户切换&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 临时忽略租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">withIgnoreStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换到指定租户执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8888"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><hr>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/backend/api.html">API 开发</a> - 学习 RESTful API 开发</li>
<li><a href="/zh/guide/backend/service.html">服务开发</a> - 学习业务服务开发</li>
<li><a href="/zh/guide/saas/">多租户</a> - 了解 SaaS 多租户开发</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[服务开发指南 ]]></title>
            <link>https://docs.battcn.com/zh/guide/backend/service.html</link>
            <guid>https://docs.battcn.com/zh/guide/backend/service.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="服务开发指南" tabindex="-1">服务开发指南 <a class="header-anchor" href="#服务开发指南" aria-label="Permalink to &quot;服务开发指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 后端服务开发的核心功能和最佳实践</p>
</div>
<h2 id="上下文获取" tabindex="-1">上下文获取 <a class="header-anchor" href="#上下文获取" aria-label="Permalink to &quot;上下文获取&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="获取当前用户信息" tabindex="-1">获取当前用户信息 <a class="header-anchor" href="#获取当前用户信息" aria-label="Permalink to &quot;获取当前用户信息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.security.domain.AuthenticationContext;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> doSomething</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前租户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户昵称</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String nickname </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nickname</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">roles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> permissions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">permissions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="忽略租户过滤" tabindex="-1">忽略租户过滤 <a class="header-anchor" href="#忽略租户过滤" aria-label="Permalink to &quot;忽略租户过滤&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.annotation.TenantIgnore;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 方法级忽略</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantIgnore</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getAllUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 TenantContextHolder 临时忽略</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setIgnore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 这里的查询不会添加租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">} </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clear</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="事务管理" tabindex="-1">事务管理 <a class="header-anchor" href="#事务管理" aria-label="Permalink to &quot;事务管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基本事务" tabindex="-1">基本事务 <a class="header-anchor" href="#基本事务" aria-label="Permalink to &quot;基本事务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 保存订单</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 保存订单明细</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> items </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getItems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            OrderItem orderItem </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            orderItem.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setOrderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item, orderItem);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderItem;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        })</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    orderItemMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insertBatch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(items);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 扣减库存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    stockService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getItems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="事务传播机制" tabindex="-1">事务传播机制 <a class="header-anchor" href="#事务传播机制" aria-label="Permalink to &quot;事务传播机制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 必须在事务中执行，没有则创建新事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.REQUIRED)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 必须在事务中执行，没有则抛出异常</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.MANDATORY)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 创建新事务，挂起当前事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.REQUIRES_NEW)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 嵌套事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.NESTED)</span></span></code></pre>
</div><h3 id="编程式事务" tabindex="-1">编程式事务 <a class="header-anchor" href="#编程式事务" aria-label="Permalink to &quot;编程式事务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TransactionTemplate transactionTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        transactionTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">execute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">                saveOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                status.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setRollbackOnly</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="分布式锁" tabindex="-1">分布式锁 <a class="header-anchor" href="#分布式锁" aria-label="Permalink to &quot;分布式锁&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="使用-redisson" tabindex="-1">使用 Redisson <a class="header-anchor" href="#使用-redisson" aria-label="Permalink to &quot;使用 Redisson&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.redisson.api.RLock;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.redisson.api.RedissonClient;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> StockService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RedissonClient redissonClient;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">productId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Integer </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">quantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String lockKey </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "stock:lock:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> productId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        RLock lock </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redissonClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(lockKey);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 尝试获取锁，最多等待10秒，锁定30秒后自动释放</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> locked </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tryLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.SECONDS);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">locked) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"系统繁忙，请稍后重试"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 扣减库存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Stock stock </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> stockMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(productId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (stock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> quantity) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"库存不足"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            stock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> quantity);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            stockMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stock);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (InterruptedException </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Thread.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentThread</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">interrupt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"获取锁被中断"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (lock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isHeldByCurrentThread</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                lock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">unlock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用注解式分布式锁" tabindex="-1">使用注解式分布式锁 <a class="header-anchor" href="#使用注解式分布式锁" aria-label="Permalink to &quot;使用注解式分布式锁&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.redis.lock.annotation.RedisLock;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "'order:create:' + #dto.userId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">expire</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 同一用户30秒内只能创建一次订单</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        doCreateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="缓存使用" tabindex="-1">缓存使用 <a class="header-anchor" href="#缓存使用" aria-label="Permalink to &quot;缓存使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="spring-cache-注解" tabindex="-1">Spring Cache 注解 <a class="header-anchor" href="#spring-cache-注解" aria-label="Permalink to &quot;Spring Cache 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DictService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查询时缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Cacheable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时清除缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时更新缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CachePut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Dict </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">save</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 清除所有缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">allEntries</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> clearAll</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="redis-操作" tabindex="-1">Redis 操作 <a class="header-anchor" href="#redis-操作" aria-label="Permalink to &quot;Redis 操作&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.data.redis.core.StringRedisTemplate;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StringRedisTemplate redisTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 字符串操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">long</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, value, timeout, TimeUnit.SECONDS);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Hash 操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setHash</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">field</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForHash</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, field, value);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Set 操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> addToSet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String... </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForSet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, values);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // List 操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pushToList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rightPush</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, value);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 删除</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="异步处理" tabindex="-1">异步处理 <a class="header-anchor" href="#异步处理" aria-label="Permalink to &quot;异步处理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="async-异步方法" tabindex="-1">@Async 异步方法 <a class="header-anchor" href="#async-异步方法" aria-label="Permalink to &quot;@Async 异步方法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> NotifyService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">to</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">subject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始发送邮件: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, to);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送邮件逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        emailClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(to, subject, content);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"邮件发送完成: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, to);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendSms</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始发送短信: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, mobile);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> smsClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mobile, content);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">completedFuture</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(result);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> NotifyService notifyService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 创建订单</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> doCreateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 异步发送通知</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendSms</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="服务调用" tabindex="-1">服务调用 <a class="header-anchor" href="#服务调用" aria-label="Permalink to &quot;服务调用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="feign-调用" tabindex="-1">Feign 调用 <a class="header-anchor" href="#feign-调用" aria-label="Permalink to &quot;Feign 调用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 定义 Feign 客户端</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "wemirr-platform-iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/batch"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Result&#x3C;List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ids</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeignClient userFeignClient;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getOrderDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderVO vo </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order, OrderVO.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 调用用户服务获取用户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userFeignClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuccess</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUserName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getNickname</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> vo;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="远程字段映射" tabindex="-1">远程字段映射 <a class="header-anchor" href="#远程字段映射" aria-label="Permalink to &quot;远程字段映射&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 <code>@Remote</code> 注解自动填充远程数据：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long userId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Remote</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">feign</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeignClient.class, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">method</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "getById"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">            sourceField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "userId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">targetField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "nickname"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String userName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="定时任务" tabindex="-1">定时任务 <a class="header-anchor" href="#定时任务" aria-label="Permalink to &quot;定时任务&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="使用-scheduled" tabindex="-1">使用 @Scheduled <a class="header-anchor" href="#使用-scheduled" aria-label="Permalink to &quot;使用 @Scheduled&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CleanTask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 每天凌晨2点执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Scheduled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cron</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "0 0 2 * * ?"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> cleanExpiredData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始清理过期数据"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 清理逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 每5分钟执行一次</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Scheduled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fixedRate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 60</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> syncData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"同步数据"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用-snail-job-分布式任务" tabindex="-1">使用 Snail Job（分布式任务） <a class="header-anchor" href="#使用-snail-job-分布式任务" aria-label="Permalink to &quot;使用 Snail Job（分布式任务）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SyncOrderTask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">JobExecutor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "syncOrderJob"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExecuteResult </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">execute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(JobArgs </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取任务参数</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String params </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> args.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getArgsStr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 执行任务逻辑</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        doSync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExecuteResult.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"同步完成"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="文件上传" tabindex="-1">文件上传 <a class="header-anchor" href="#文件上传" aria-label="Permalink to &quot;文件上传&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/files"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FileController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OssTemplate ossTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/upload"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">upload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultipartFile </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 上传文件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String url </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ossTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">upload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(file);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(url);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ossTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(url);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="消息推送" tabindex="-1">消息推送 <a class="header-anchor" href="#消息推送" aria-label="Permalink to &quot;消息推送&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="websocket-推送" tabindex="-1">WebSocket 推送 <a class="header-anchor" href="#websocket-推送" aria-label="Permalink to &quot;WebSocket 推送&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MessageService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WebSocketTemplate webSocketTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 推送给指定用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        webSocketTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 广播消息</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        webSocketTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-参数校验" tabindex="-1">1. 参数校验 <a class="header-anchor" href="#_1-参数校验" aria-label="Permalink to &quot;1. 参数校验&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用分组校验</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">groups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Create.class)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">groups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Update.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">groups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {Create.class, Update.class})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Create.class) UserDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Update.class) UserDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_2-异常处理" tabindex="-1">2. 异常处理 <a class="header-anchor" href="#_2-异常处理" aria-label="Permalink to &quot;2. 异常处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 抛出业务异常</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 断言式异常</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户已被禁用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h3 id="_3-日志规范" tabindex="-1">3. 日志规范 <a class="header-anchor" href="#_3-日志规范" aria-label="Permalink to &quot;3. 日志规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始创建订单, userId={}, productId={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单详情: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单失败, dto={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dto, e);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功, orderId={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/saas/">多租户</a> - 了解 SaaS 多租户开发</li>
<li><a href="/zh/guide/packages/data_permission.html">数据权限</a> - 学习数据权限控制</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="服务开发指南" tabindex="-1">服务开发指南 <a class="header-anchor" href="#服务开发指南" aria-label="Permalink to &quot;服务开发指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 后端服务开发的核心功能和最佳实践</p>
</div>
<h2 id="上下文获取" tabindex="-1">上下文获取 <a class="header-anchor" href="#上下文获取" aria-label="Permalink to &quot;上下文获取&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="获取当前用户信息" tabindex="-1">获取当前用户信息 <a class="header-anchor" href="#获取当前用户信息" aria-label="Permalink to &quot;获取当前用户信息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.security.domain.AuthenticationContext;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> doSomething</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前租户ID</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户昵称</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String nickname </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nickname</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">roles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取当前用户权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> permissions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">permissions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="忽略租户过滤" tabindex="-1">忽略租户过滤 <a class="header-anchor" href="#忽略租户过滤" aria-label="Permalink to &quot;忽略租户过滤&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.db.annotation.TenantIgnore;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 方法级忽略</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantIgnore</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getAllUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用 TenantContextHolder 临时忽略</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setIgnore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 这里的查询不会添加租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">} </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clear</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="事务管理" tabindex="-1">事务管理 <a class="header-anchor" href="#事务管理" aria-label="Permalink to &quot;事务管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基本事务" tabindex="-1">基本事务 <a class="header-anchor" href="#基本事务" aria-label="Permalink to &quot;基本事务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 保存订单</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 保存订单明细</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> items </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getItems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            OrderItem orderItem </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            orderItem.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setOrderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item, orderItem);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderItem;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        })</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    orderItemMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insertBatch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(items);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 扣减库存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    stockService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">deduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getItems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="事务传播机制" tabindex="-1">事务传播机制 <a class="header-anchor" href="#事务传播机制" aria-label="Permalink to &quot;事务传播机制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 必须在事务中执行，没有则创建新事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.REQUIRED)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 必须在事务中执行，没有则抛出异常</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.MANDATORY)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 创建新事务，挂起当前事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.REQUIRES_NEW)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 嵌套事务</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">propagation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Propagation.NESTED)</span></span></code></pre>
</div><h3 id="编程式事务" tabindex="-1">编程式事务 <a class="header-anchor" href="#编程式事务" aria-label="Permalink to &quot;编程式事务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TransactionTemplate transactionTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        transactionTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">execute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">                saveOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                status.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setRollbackOnly</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="分布式锁" tabindex="-1">分布式锁 <a class="header-anchor" href="#分布式锁" aria-label="Permalink to &quot;分布式锁&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="使用-redisson" tabindex="-1">使用 Redisson <a class="header-anchor" href="#使用-redisson" aria-label="Permalink to &quot;使用 Redisson&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.redisson.api.RLock;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.redisson.api.RedissonClient;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> StockService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RedissonClient redissonClient;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">productId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Integer </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">quantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String lockKey </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "stock:lock:"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> productId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        RLock lock </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redissonClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(lockKey);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 尝试获取锁，最多等待10秒，锁定30秒后自动释放</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> locked </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tryLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, TimeUnit.SECONDS);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">locked) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"系统繁忙，请稍后重试"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 扣减库存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Stock stock </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> stockMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(productId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (stock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> quantity) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"库存不足"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            stock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getQuantity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> quantity);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            stockMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(stock);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (InterruptedException </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Thread.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentThread</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">interrupt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"获取锁被中断"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (lock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isHeldByCurrentThread</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                lock.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">unlock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用注解式分布式锁" tabindex="-1">使用注解式分布式锁 <a class="header-anchor" href="#使用注解式分布式锁" aria-label="Permalink to &quot;使用注解式分布式锁&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.redis.lock.annotation.RedisLock;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "'order:create:' + #dto.userId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">expire</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 同一用户30秒内只能创建一次订单</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        doCreateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="缓存使用" tabindex="-1">缓存使用 <a class="header-anchor" href="#缓存使用" aria-label="Permalink to &quot;缓存使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="spring-cache-注解" tabindex="-1">Spring Cache 注解 <a class="header-anchor" href="#spring-cache-注解" aria-label="Permalink to &quot;Spring Cache 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DictService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查询时缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Cacheable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DictItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(code);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时清除缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 更新时更新缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CachePut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">key</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "#dto.code"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Dict </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">save</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DictDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dto;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 清除所有缓存</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CacheEvict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">allEntries</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> clearAll</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="redis-操作" tabindex="-1">Redis 操作 <a class="header-anchor" href="#redis-操作" aria-label="Permalink to &quot;Redis 操作&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.data.redis.core.StringRedisTemplate;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> StringRedisTemplate redisTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 字符串操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">long</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, value, timeout, TimeUnit.SECONDS);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Hash 操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setHash</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">field</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForHash</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, field, value);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Set 操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> addToSet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String... </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForSet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, values);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // List 操作</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pushToList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">opsForList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rightPush</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key, value);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 删除</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        redisTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(key);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="异步处理" tabindex="-1">异步处理 <a class="header-anchor" href="#异步处理" aria-label="Permalink to &quot;异步处理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="async-异步方法" tabindex="-1">@Async 异步方法 <a class="header-anchor" href="#async-异步方法" aria-label="Permalink to &quot;@Async 异步方法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> NotifyService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">to</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">subject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始发送邮件: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, to);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送邮件逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        emailClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(to, subject, content);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"邮件发送完成: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, to);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Async</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendSms</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始发送短信: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, mobile);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> smsClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mobile, content);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CompletableFuture.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">completedFuture</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(result);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> NotifyService notifyService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 创建订单</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> doCreateOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 异步发送通知</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getEmail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendSms</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMobile</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"..."</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="服务调用" tabindex="-1">服务调用 <a class="header-anchor" href="#服务调用" aria-label="Permalink to &quot;服务调用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="feign-调用" tabindex="-1">Feign 调用 <a class="header-anchor" href="#feign-调用" aria-label="Permalink to &quot;Feign 调用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 定义 Feign 客户端</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "wemirr-platform-iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/batch"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Result&#x3C;List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ids</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeignClient userFeignClient;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getOrderDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orderId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orderId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        OrderVO vo </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order, OrderVO.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 调用用户服务获取用户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userFeignClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuccess</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            vo.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUserName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getNickname</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> vo;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="远程字段映射" tabindex="-1">远程字段映射 <a class="header-anchor" href="#远程字段映射" aria-label="Permalink to &quot;远程字段映射&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 <code>@Remote</code> 注解自动填充远程数据：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long userId;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Remote</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">feign</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeignClient.class, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">method</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "getById"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">            sourceField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "userId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">targetField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "nickname"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String userName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="定时任务" tabindex="-1">定时任务 <a class="header-anchor" href="#定时任务" aria-label="Permalink to &quot;定时任务&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="使用-scheduled" tabindex="-1">使用 @Scheduled <a class="header-anchor" href="#使用-scheduled" aria-label="Permalink to &quot;使用 @Scheduled&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CleanTask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 每天凌晨2点执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Scheduled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cron</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "0 0 2 * * ?"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> cleanExpiredData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始清理过期数据"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 清理逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 每5分钟执行一次</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Scheduled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fixedRate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 60</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> syncData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"同步数据"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用-snail-job-分布式任务" tabindex="-1">使用 Snail Job（分布式任务） <a class="header-anchor" href="#使用-snail-job-分布式任务" aria-label="Permalink to &quot;使用 Snail Job（分布式任务）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SyncOrderTask</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">JobExecutor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "syncOrderJob"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExecuteResult </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">execute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(JobArgs </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取任务参数</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String params </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> args.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getArgsStr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 执行任务逻辑</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        doSync</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExecuteResult.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"同步完成"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="文件上传" tabindex="-1">文件上传 <a class="header-anchor" href="#文件上传" aria-label="Permalink to &quot;文件上传&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/files"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FileController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OssTemplate ossTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/upload"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">upload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultipartFile </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 上传文件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String url </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ossTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">upload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(file);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(url);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ossTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(url);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="消息推送" tabindex="-1">消息推送 <a class="header-anchor" href="#消息推送" aria-label="Permalink to &quot;消息推送&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="websocket-推送" tabindex="-1">WebSocket 推送 <a class="header-anchor" href="#websocket-推送" aria-label="Permalink to &quot;WebSocket 推送&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MessageService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WebSocketTemplate webSocketTemplate;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 推送给指定用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        webSocketTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 广播消息</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        webSocketTemplate.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-参数校验" tabindex="-1">1. 参数校验 <a class="header-anchor" href="#_1-参数校验" aria-label="Permalink to &quot;1. 参数校验&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用分组校验</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserDTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">groups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Create.class)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">groups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Update.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">NotBlank</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">groups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {Create.class, Update.class})</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Create.class) UserDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Validated</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Update.class) UserDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="_2-异常处理" tabindex="-1">2. 异常处理 <a class="header-anchor" href="#_2-异常处理" aria-label="Permalink to &quot;2. 异常处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 抛出业务异常</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 断言式异常</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户已被禁用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h3 id="_3-日志规范" tabindex="-1">3. 日志规范 <a class="header-anchor" href="#_3-日志规范" aria-label="Permalink to &quot;3. 日志规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"开始创建订单, userId={}, productId={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 业务逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单详情: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Exception </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建订单失败, dto={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dto, e);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"订单创建成功, orderId={}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, order.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/saas/">多租户</a> - 了解 SaaS 多租户开发</li>
<li><a href="/zh/guide/packages/data_permission.html">数据权限</a> - 学习数据权限控制</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[配置 ]]></title>
            <link>https://docs.battcn.com/zh/guide/config.html</link>
            <guid>https://docs.battcn.com/zh/guide/config.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="我还没想好怎么写" tabindex="-1">我还没想好怎么写 <a class="header-anchor" href="#我还没想好怎么写" aria-label="Permalink to &quot;我还没想好怎么写&quot;">&ZeroWidthSpace;</a></h2>
]]></description>
            <content:encoded><![CDATA[<h1 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="我还没想好怎么写" tabindex="-1">我还没想好怎么写 <a class="header-anchor" href="#我还没想好怎么写" aria-label="Permalink to &quot;我还没想好怎么写&quot;">&ZeroWidthSpace;</a></h2>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[简单案例 ]]></title>
            <link>https://docs.battcn.com/zh/guide/dev/demo.html</link>
            <guid>https://docs.battcn.com/zh/guide/dev/demo.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="简单案例" tabindex="-1">简单案例 <a class="header-anchor" href="#简单案例" aria-label="Permalink to &quot;简单案例&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">增删改查</p>
<p><code>WEMIRR-PLATFORM</code> 项目与 <code>fast-crud</code> 深度集成合作，大大降低了前端小伙伴入门门槛，只需要简单的配置就可以实现精美的表单和表格</p>
<p>下面通过一个小 <code>DEMO</code> 来给大家讲解如何在3分钟内快速上手开发自己的分页+增删改查</p>
</div>
<h2 id="前端实现" tabindex="-1">前端实现 <a class="header-anchor" href="#前端实现" aria-label="Permalink to &quot;前端实现&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>参考下面模板,创建<code>index.vue</code> 和 <code>crud.tsx</code> 模板，然后重点是调整 <code>crud.tsx</code> 模板部分</strong></p>
<h3 id="index-vue" tabindex="-1"><code>index.vue</code> <a class="header-anchor" href="#index-vue" aria-label="Permalink to &quot;`index.vue`&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"DemoPage"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">import { onMounted } from 'vue';</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">import { useFs } from '@fast-crud/fast-crud';</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">import createCrudOptions from './crud';</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">// 通过context传递到crud.tsx中</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">const { crudBinding, crudRef, crudExpose } = useFs({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  createCrudOptions,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 设置权限前缀,当然也可以给按钮单独设置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  context: { permission: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'sys:demo'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">// 页面打开后获取列表数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">onMounted(async () => {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> crudExpose.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">doRefresh</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fs-page</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"page-layout-card"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudBinding"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fs-page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="crud-tsx" tabindex="-1"><code>crud.tsx</code> <a class="header-anchor" href="#crud-tsx" aria-label="Permalink to &quot;`crud.tsx`&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-tsx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">tsx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { dict, utils } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { defHttp } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '#/api/request'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> crud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  props</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreateCrudOptionsProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreateCrudOptionsRet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  utils.logger.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'crud props'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, props);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/page`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { params: query });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AddReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/create`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> EditReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">form</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}/modify`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DelReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">row</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        id: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">150</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            rules: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'岗位名称不能为空'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { min: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, max: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'长度在 1 到 30 个字符'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-radio'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, align: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'center'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          addForm: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          dict: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          }),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        description: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'描述'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'textarea'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            col: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">24</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">220</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ellipsis: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        createdTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'wp-readonly-time'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="后端实现" tabindex="-1">后端实现 <a class="header-anchor" href="#后端实现" aria-label="Permalink to &quot;后端实现&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>后端同学只要写好响应的增删改查方法即可，这里就不在过多赘述了</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "接口名称 - [Levin] - [DONE]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">分页返回实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PageRequest request) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> service.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">数据库实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(数据库实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getSysOrgId, orgId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orderByAsc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(数据库实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getSort))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">convert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(x </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(x, 分页返回实体.class));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="简单案例" tabindex="-1">简单案例 <a class="header-anchor" href="#简单案例" aria-label="Permalink to &quot;简单案例&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">增删改查</p>
<p><code>WEMIRR-PLATFORM</code> 项目与 <code>fast-crud</code> 深度集成合作，大大降低了前端小伙伴入门门槛，只需要简单的配置就可以实现精美的表单和表格</p>
<p>下面通过一个小 <code>DEMO</code> 来给大家讲解如何在3分钟内快速上手开发自己的分页+增删改查</p>
</div>
<h2 id="前端实现" tabindex="-1">前端实现 <a class="header-anchor" href="#前端实现" aria-label="Permalink to &quot;前端实现&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>参考下面模板,创建<code>index.vue</code> 和 <code>crud.tsx</code> 模板，然后重点是调整 <code>crud.tsx</code> 模板部分</strong></p>
<h3 id="index-vue" tabindex="-1"><code>index.vue</code> <a class="header-anchor" href="#index-vue" aria-label="Permalink to &quot;`index.vue`&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"DemoPage"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">import { onMounted } from 'vue';</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">import { useFs } from '@fast-crud/fast-crud';</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">import createCrudOptions from './crud';</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">// 通过context传递到crud.tsx中</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">const { crudBinding, crudRef, crudExpose } = useFs({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  createCrudOptions,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 设置权限前缀,当然也可以给按钮单独设置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  context: { permission: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'sys:demo'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">// 页面打开后获取列表数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">onMounted(async () => {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> crudExpose.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">doRefresh</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fs-page</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"page-layout-card"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudBinding"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fs-page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="crud-tsx" tabindex="-1"><code>crud.tsx</code> <a class="header-anchor" href="#crud-tsx" aria-label="Permalink to &quot;`crud.tsx`&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-tsx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">tsx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { dict, utils } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { defHttp } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '#/api/request'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> crud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  props</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreateCrudOptionsProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreateCrudOptionsRet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  utils.logger.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'crud props'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, props);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/page`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { params: query });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AddReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/create`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> EditReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">form</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}/modify`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DelReq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> defHttp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/demo/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">row</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        id: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">150</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            rules: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'岗位名称不能为空'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { min: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, max: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'长度在 1 到 30 个字符'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-radio'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, align: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'center'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          addForm: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          dict: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">              { value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          }),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        description: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'描述'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'textarea'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            col: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">24</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">220</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ellipsis: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        createdTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'wp-readonly-time'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="后端实现" tabindex="-1">后端实现 <a class="header-anchor" href="#后端实现" aria-label="Permalink to &quot;后端实现&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>后端同学只要写好响应的增删改查方法即可，这里就不在过多赘述了</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "接口名称 - [Levin] - [DONE]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">分页返回实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PageRequest request) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> service.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">数据库实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(数据库实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getSysOrgId, orgId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orderByAsc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(数据库实体</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getSort))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">convert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(x </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(x, 分页返回实体.class));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[快速开发 ]]></title>
            <link>https://docs.battcn.com/zh/guide/dev/</link>
            <guid>https://docs.battcn.com/zh/guide/dev/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="快速开发" tabindex="-1">快速开发 <a class="header-anchor" href="#快速开发" aria-label="Permalink to &quot;快速开发&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>本章节很重要，请认真阅读</p>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="快速开发" tabindex="-1">快速开发 <a class="header-anchor" href="#快速开发" aria-label="Permalink to &quot;快速开发&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>本章节很重要，请认真阅读</p>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[系统日志 ]]></title>
            <link>https://docs.battcn.com/zh/guide/dev/log.html</link>
            <guid>https://docs.battcn.com/zh/guide/dev/log.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="系统日志" tabindex="-1">系统日志 <a class="header-anchor" href="#系统日志" aria-label="Permalink to &quot;系统日志&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>在实际开发中，对于某些关键业务，我们通常需要记录该操作的内容，一个操作调一次记录方法，每次还得去收集参数等等，会造成大量代码重复。
我们希望代码中只有业务相关的操作，在项目中使用注解来完成此项功能。</p>
</div>
<h2 id="日志采集" tabindex="-1">日志采集 <a class="header-anchor" href="#日志采集" aria-label="Permalink to &quot;日志采集&quot;">&ZeroWidthSpace;</a></h2>
<p>如果需要在服务中将接口请求的日志信息记录下来，请在接口上添加 <code>@AccessLog</code> 注解</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserSaveReq req){</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>支持参数如下：</p>
<table tabindex="0">
<thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>默认值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>module</td>
<td>String</td>
<td>无</td>
<td>所属模块</td>
</tr>
<tr>
<td>description</td>
<td>String</td>
<td>无</td>
<td>描述</td>
</tr>
<tr>
<td>request</td>
<td>boolean</td>
<td>true</td>
<td>记录执行参数</td>
</tr>
<tr>
<td>response</td>
<td>boolean</td>
<td>true</td>
<td>记录返回参数</td>
</tr>
</tbody>
</table>
<h2 id="属性配置" tabindex="-1">属性配置 <a class="header-anchor" href="#属性配置" aria-label="Permalink to &quot;属性配置&quot;">&ZeroWidthSpace;</a></h2>
<p>默认开启：日志可以自定义配置启动关闭 <code>extend.boot.log.enabled = false</code></p>
<div class="language-properties vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">properties</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">extend:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  boot:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    log:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 如果不是 iam 服务 需要添加如下配置开启 feign 策略采集</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      strategy: feign</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="系统日志" tabindex="-1">系统日志 <a class="header-anchor" href="#系统日志" aria-label="Permalink to &quot;系统日志&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>在实际开发中，对于某些关键业务，我们通常需要记录该操作的内容，一个操作调一次记录方法，每次还得去收集参数等等，会造成大量代码重复。
我们希望代码中只有业务相关的操作，在项目中使用注解来完成此项功能。</p>
</div>
<h2 id="日志采集" tabindex="-1">日志采集 <a class="header-anchor" href="#日志采集" aria-label="Permalink to &quot;日志采集&quot;">&ZeroWidthSpace;</a></h2>
<p>如果需要在服务中将接口请求的日志信息记录下来，请在接口上添加 <code>@AccessLog</code> 注解</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "添加用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserSaveReq req){</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>支持参数如下：</p>
<table tabindex="0">
<thead>
<tr>
<th>参数</th>
<th>类型</th>
<th>默认值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>module</td>
<td>String</td>
<td>无</td>
<td>所属模块</td>
</tr>
<tr>
<td>description</td>
<td>String</td>
<td>无</td>
<td>描述</td>
</tr>
<tr>
<td>request</td>
<td>boolean</td>
<td>true</td>
<td>记录执行参数</td>
</tr>
<tr>
<td>response</td>
<td>boolean</td>
<td>true</td>
<td>记录返回参数</td>
</tr>
</tbody>
</table>
<h2 id="属性配置" tabindex="-1">属性配置 <a class="header-anchor" href="#属性配置" aria-label="Permalink to &quot;属性配置&quot;">&ZeroWidthSpace;</a></h2>
<p>默认开启：日志可以自定义配置启动关闭 <code>extend.boot.log.enabled = false</code></p>
<div class="language-properties vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">properties</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">extend:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  boot:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    log:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 如果不是 iam 服务 需要添加如下配置开启 feign 策略采集</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      strategy: feign</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[事务管理 ]]></title>
            <link>https://docs.battcn.com/zh/guide/dev/transactional.html</link>
            <guid>https://docs.battcn.com/zh/guide/dev/transactional.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="事务管理" tabindex="-1">事务管理 <a class="header-anchor" href="#事务管理" aria-label="Permalink to &quot;事务管理&quot;">&ZeroWidthSpace;</a></h1>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>事务注解只能应用到公共可见度的方法上，可以被应用于接口定义和接口方法，方法会覆盖类上面声明的事务。</p>
<p>如果使用数据源隔离或者多数据源请用 <code>@DSTransactional</code> 反之用 <code>@Transactional</code> 或者 <code>@DSTransactional</code> 都可以</p>
</div>
<h2 id="案例讲解" tabindex="-1">案例讲解 <a class="header-anchor" href="#案例讲解" aria-label="Permalink to &quot;案例讲解&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>例如用户新增需要插入用户表、用户与角色关联表，如果插入成功，那么一起成功，如果中间有一条出现异常，那么回滚之前的所有操作。</strong></p>
<p><strong>为了防止出现脏数据，就需要借助 Spring 事务管理器来让它实现回退。</strong></p>
<h3 id="单一数据源" tabindex="-1">单一数据源 <a class="header-anchor" href="#单一数据源" aria-label="Permalink to &quot;单一数据源&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>JDBC 链接不能用 <code>spring.datasource.dynamic</code> 方式，否则事务容易失效 得用 <code>spring.datasource.jdbc</code> 自带配置方式</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User user){</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户与角色管理</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="动态数据源" tabindex="-1">动态数据源 <a class="header-anchor" href="#动态数据源" aria-label="Permalink to &quot;动态数据源&quot;">&ZeroWidthSpace;</a></h3>
<div class="danger custom-block"><p class="custom-block-title">注意</p>
<p>动态数据源或者多数据必须用它，不然事务不会生效</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User user){</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户与角色管理</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="事务特性" tabindex="-1">事务特性 <a class="header-anchor" href="#事务特性" aria-label="Permalink to &quot;事务特性&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>更多事务的内容可以自行 <code>Google</code> 或者看 <code>Spring</code> 官方文档</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>propagation</td>
<td>事务的传播行为，默认值为 REQUIRED。</td>
</tr>
<tr>
<td>isolation</td>
<td>事务的隔离度，默认值采用 DEFAULT</td>
</tr>
<tr>
<td>timeout</td>
<td>事务的超时时间，默认值为-1，不超时。如果设置了超时时间(单位秒)，那么如果超过该时间限制了但事务还没有完成，则自动回滚事务。</td>
</tr>
<tr>
<td>read-only</td>
<td>指定事务是否为只读事务，默认值为 false；为了忽略那些不需要事务的方法，比如读取数据，可以设置 read-only 为 true。</td>
</tr>
<tr>
<td>rollbackFor</td>
<td>用于指定能够触发事务回滚的异常类型，如果有多个异常类型需要指定，各类型之间可以通过逗号分隔。</td>
</tr>
<tr>
<td>noRollbackFor</td>
<td>抛出 no-rollback-for 指定的异常类型，不回滚事务。</td>
</tr>
</tbody>
</table>
<div class="tip custom-block"><p class="custom-block-title">提示</p>
<p>事务的传播机制是指如果在开始当前事务之前，一个事务上下文已经存在，此时有若干选项可以指定一个事务性方法的执行行为。 即:在执行一个@Transactinal注解标注的方法时，开启了事务；当该方法还在执行中时，另一个人也触发了该方法；那么此时怎么算事务呢，这时就可以通过事务的传播机制来指定处理方式。</p>
</div>
<p><code>TransactionDefinition</code>传播行为的常量：</p>
<table tabindex="0">
<thead>
<tr>
<th>常量</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>TransactionDefinition.PROPAGATION_REQUIRED</td>
<td>事务的传播行为，默认值为 REQUIRED。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_REQUIRES_NEW</td>
<td>事务的隔离度，默认值采用 DEFAULT</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_SUPPORTS</td>
<td>事务的超时时间，默认值为-1，不超时。如果设置了超时时间(单位秒)，那么如果超过该时间限制了但事务还没有完成，则自动回滚事务。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_NOT_SUPPORTED</td>
<td>指定事务是否为只读事务，默认值为 false；为了忽略那些不需要事务的方法，比如读取数据，可以设置 read-only 为 true。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_NEVER</td>
<td>用于指定能够触发事务回滚的异常类型，如果有多个异常类型需要指定，各类型之间可以通过逗号分隔。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_MANDATORY</td>
<td>抛出 no-rollback-for 指定的异常类型，不回滚事务。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_NESTED</td>
<td>抛出 no-rollback-for 指定的异常类型，不回滚事务。</td>
</tr>
</tbody>
</table>
]]></description>
            <content:encoded><![CDATA[<h1 id="事务管理" tabindex="-1">事务管理 <a class="header-anchor" href="#事务管理" aria-label="Permalink to &quot;事务管理&quot;">&ZeroWidthSpace;</a></h1>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>事务注解只能应用到公共可见度的方法上，可以被应用于接口定义和接口方法，方法会覆盖类上面声明的事务。</p>
<p>如果使用数据源隔离或者多数据源请用 <code>@DSTransactional</code> 反之用 <code>@Transactional</code> 或者 <code>@DSTransactional</code> 都可以</p>
</div>
<h2 id="案例讲解" tabindex="-1">案例讲解 <a class="header-anchor" href="#案例讲解" aria-label="Permalink to &quot;案例讲解&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>例如用户新增需要插入用户表、用户与角色关联表，如果插入成功，那么一起成功，如果中间有一条出现异常，那么回滚之前的所有操作。</strong></p>
<p><strong>为了防止出现脏数据，就需要借助 Spring 事务管理器来让它实现回退。</strong></p>
<h3 id="单一数据源" tabindex="-1">单一数据源 <a class="header-anchor" href="#单一数据源" aria-label="Permalink to &quot;单一数据源&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>JDBC 链接不能用 <code>spring.datasource.dynamic</code> 方式，否则事务容易失效 得用 <code>spring.datasource.jdbc</code> 自带配置方式</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User user){</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户与角色管理</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="动态数据源" tabindex="-1">动态数据源 <a class="header-anchor" href="#动态数据源" aria-label="Permalink to &quot;动态数据源&quot;">&ZeroWidthSpace;</a></h3>
<div class="danger custom-block"><p class="custom-block-title">注意</p>
<p>动态数据源或者多数据必须用它，不然事务不会生效</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User user){</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">	// 新增用户与角色管理</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="事务特性" tabindex="-1">事务特性 <a class="header-anchor" href="#事务特性" aria-label="Permalink to &quot;事务特性&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>更多事务的内容可以自行 <code>Google</code> 或者看 <code>Spring</code> 官方文档</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>propagation</td>
<td>事务的传播行为，默认值为 REQUIRED。</td>
</tr>
<tr>
<td>isolation</td>
<td>事务的隔离度，默认值采用 DEFAULT</td>
</tr>
<tr>
<td>timeout</td>
<td>事务的超时时间，默认值为-1，不超时。如果设置了超时时间(单位秒)，那么如果超过该时间限制了但事务还没有完成，则自动回滚事务。</td>
</tr>
<tr>
<td>read-only</td>
<td>指定事务是否为只读事务，默认值为 false；为了忽略那些不需要事务的方法，比如读取数据，可以设置 read-only 为 true。</td>
</tr>
<tr>
<td>rollbackFor</td>
<td>用于指定能够触发事务回滚的异常类型，如果有多个异常类型需要指定，各类型之间可以通过逗号分隔。</td>
</tr>
<tr>
<td>noRollbackFor</td>
<td>抛出 no-rollback-for 指定的异常类型，不回滚事务。</td>
</tr>
</tbody>
</table>
<div class="tip custom-block"><p class="custom-block-title">提示</p>
<p>事务的传播机制是指如果在开始当前事务之前，一个事务上下文已经存在，此时有若干选项可以指定一个事务性方法的执行行为。 即:在执行一个@Transactinal注解标注的方法时，开启了事务；当该方法还在执行中时，另一个人也触发了该方法；那么此时怎么算事务呢，这时就可以通过事务的传播机制来指定处理方式。</p>
</div>
<p><code>TransactionDefinition</code>传播行为的常量：</p>
<table tabindex="0">
<thead>
<tr>
<th>常量</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>TransactionDefinition.PROPAGATION_REQUIRED</td>
<td>事务的传播行为，默认值为 REQUIRED。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_REQUIRES_NEW</td>
<td>事务的隔离度，默认值采用 DEFAULT</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_SUPPORTS</td>
<td>事务的超时时间，默认值为-1，不超时。如果设置了超时时间(单位秒)，那么如果超过该时间限制了但事务还没有完成，则自动回滚事务。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_NOT_SUPPORTED</td>
<td>指定事务是否为只读事务，默认值为 false；为了忽略那些不需要事务的方法，比如读取数据，可以设置 read-only 为 true。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_NEVER</td>
<td>用于指定能够触发事务回滚的异常类型，如果有多个异常类型需要指定，各类型之间可以通过逗号分隔。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_MANDATORY</td>
<td>抛出 no-rollback-for 指定的异常类型，不回滚事务。</td>
</tr>
<tr>
<td>TransactionDefinition.PROPAGATION_NESTED</td>
<td>抛出 no-rollback-for 指定的异常类型，不回滚事务。</td>
</tr>
</tbody>
</table>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[项目结构 ]]></title>
            <link>https://docs.battcn.com/zh/guide/directory.html</link>
            <guid>https://docs.battcn.com/zh/guide/directory.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="项目结构" tabindex="-1">项目结构 <a class="header-anchor" href="#项目结构" aria-label="Permalink to &quot;项目结构&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">源码地址</p>
<ul>
<li><strong>后端仓库</strong>：<a href="https://gitee.com/battcn/wemirr-platform" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform</a> （分支：v4-dev）</li>
<li><strong>前端仓库</strong>：<a href="https://gitee.com/battcn/wemirr-platform-ui" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform-ui</a> （分支：v4-dev）</li>
</ul>
</div>
<h2 id="后端项目结构" tabindex="-1">后端项目结构 <a class="header-anchor" href="#后端项目结构" aria-label="Permalink to &quot;后端项目结构&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>wemirr-platform</strong> 采用 Maven 多模块架构，基于 Spring Cloud Alibaba 2023 构建。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform/</span></span>
<span class="line"><span>├── wemirr-platform-dependencies/       # 📦 依赖版本管理（BOM）</span></span>
<span class="line"><span>├── wemirr-platform-framework/          # 🔧 框架核心层</span></span>
<span class="line"><span>│   ├── common-framework-core/          # 基础核心（注解、接口、工具类）</span></span>
<span class="line"><span>│   ├── common-spring-boot-starter/     # Spring Boot 核心启动器</span></span>
<span class="line"><span>│   ├── db-spring-boot-starter/         # 数据库增强（MyBatis-Plus、多租户、数据权限）</span></span>
<span class="line"><span>│   ├── redis-plus-spring-boot-starter/ # Redis 缓存增强</span></span>
<span class="line"><span>│   ├── security-spring-boot-starter/   # 安全认证（Sa-Token）</span></span>
<span class="line"><span>│   ├── feign-plugin-spring-boot-starter/ # Feign 增强（Token传递、服务代理）</span></span>
<span class="line"><span>│   ├── easyexcel-spring-boot-starter/  # Excel 导入导出</span></span>
<span class="line"><span>│   ├── diff-log-spring-boot-starter/   # 差异日志</span></span>
<span class="line"><span>│   ├── i18n-spring-boot-starter/       # 国际化</span></span>
<span class="line"><span>│   ├── mongodb-plus-spring-boot-starter/ # MongoDB 增强</span></span>
<span class="line"><span>│   ├── pdf-spring-boot-starter/        # PDF 生成</span></span>
<span class="line"><span>│   ├── robot-spring-boot-starter/      # 消息机器人（钉钉、企微、飞书）</span></span>
<span class="line"><span>│   └── websocket-spring-boot-starter/  # 分布式 WebSocket</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-feign/              # 🔗 Feign 接口定义</span></span>
<span class="line"><span>│   ├── wemirr-platform-iam-api/        # IAM 服务 API</span></span>
<span class="line"><span>│   ├── wemirr-platform-suite-api/      # Suite 服务 API</span></span>
<span class="line"><span>│   └── wemirr-platform-workflow-api/   # 工作流服务 API</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-iam/                # 🔐 IAM 认证授权中心</span></span>
<span class="line"><span>│   └── src/main/java/.../iam/</span></span>
<span class="line"><span>│       ├── auth/                       # 认证模块</span></span>
<span class="line"><span>│       ├── base/                       # 基础模块</span></span>
<span class="line"><span>│       ├── system/                     # 系统管理</span></span>
<span class="line"><span>│       │   ├── controller/             # 控制器</span></span>
<span class="line"><span>│       │   ├── domain/                 # 领域模型</span></span>
<span class="line"><span>│       │   ├── repository/             # 数据访问</span></span>
<span class="line"><span>│       │   ├── service/                # 业务逻辑</span></span>
<span class="line"><span>│       │   └── func/                   # 功能函数</span></span>
<span class="line"><span>│       └── tenant/                     # 租户管理</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-suite/              # 💼 业务套件中心</span></span>
<span class="line"><span>│   └── src/main/java/.../suite/        # 业务功能实现</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-plugin/                      # 🔌 插件扩展中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-ai/             # AI 能力（Langchain4j）</span></span>
<span class="line"><span>│   ├── wemirr-platform-workflow/       # 审批流程（Warm-Flow）</span></span>
<span class="line"><span>│   ├── wemirr-platform-monitor/        # 监控中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-tms/            # 运输管理（TMS）</span></span>
<span class="line"><span>│   └── wemirr-platform-wms/            # 仓储管理（WMS）</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-gateway/            # 🚪 API 网关</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── dev-support/                        # 🛠️ 开发支持</span></span>
<span class="line"><span>├── docs/                               # 📚 文档</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>└── 附件/                                # 📁 附件资源</span></span>
<span class="line"><span>    ├── docker/                         # Docker 编排文件</span></span>
<span class="line"><span>    ├── mysql/                          # 数据库脚本</span></span>
<span class="line"><span>    ├── nacos/                          # Nacos 配置文件</span></span>
<span class="line"><span>    └── nginx/                          # Nginx 配置</span></span></code></pre>
</div><h3 id="核心模块说明" tabindex="-1">核心模块说明 <a class="header-anchor" href="#核心模块说明" aria-label="Permalink to &quot;核心模块说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>模块</th>
<th>说明</th>
<th>技术栈</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>wemirr-platform-iam</strong></td>
<td>IAM 认证授权中心，用户、角色、菜单、租户管理</td>
<td>Sa-Token、MyBatis-Plus</td>
</tr>
<tr>
<td><strong>wemirr-platform-suite</strong></td>
<td>业务套件，存放核心业务功能</td>
<td>Spring Boot</td>
</tr>
<tr>
<td><strong>wemirr-plugin</strong></td>
<td>插件中心，可插拔的扩展功能（AI、工作流、WMS、TMS）</td>
<td>Warm-Flow、Langchain4j</td>
</tr>
<tr>
<td><strong>wemirr-platform-gateway</strong></td>
<td>API 网关，统一入口、限流、鉴权</td>
<td>Spring Cloud Gateway</td>
</tr>
<tr>
<td><strong>wemirr-platform-framework</strong></td>
<td>框架层，提供通用能力封装</td>
<td>多个 Spring Boot Starter</td>
</tr>
<tr>
<td><strong>wemirr-platform-feign</strong></td>
<td>Feign 接口定义，服务间调用</td>
<td>OpenFeign</td>
</tr>
</tbody>
</table>
<h2 id="前端项目结构" tabindex="-1">前端项目结构 <a class="header-anchor" href="#前端项目结构" aria-label="Permalink to &quot;前端项目结构&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>wemirr-platform-ui</strong> 基于 <strong>Vben Admin 5.x</strong> + Vue 3 + Ant Design Vue 构建，采用 <strong>Monorepo</strong> 架构。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform-ui/</span></span>
<span class="line"><span>├── apps/                               # 📱 应用目录</span></span>
<span class="line"><span>│   └── web-antd/                       # Ant Design Vue 版本</span></span>
<span class="line"><span>│       ├── src/</span></span>
<span class="line"><span>│       │   ├── api/                    # 🌐 API 接口</span></span>
<span class="line"><span>│       │   │   ├── core/               # 核心接口</span></span>
<span class="line"><span>│       │   │   ├── system/             # 系统接口</span></span>
<span class="line"><span>│       │   │   │   ├── oss/            # 文件存储</span></span>
<span class="line"><span>│       │   │   │   └── user/           # 用户管理</span></span>
<span class="line"><span>│       │   │   └── workflow/           # 工作流接口</span></span>
<span class="line"><span>│       │   │</span></span>
<span class="line"><span>│       │   ├── views/                  # 📄 页面视图</span></span>
<span class="line"><span>│       │   │   ├── _core/              # 核心页面（登录等）</span></span>
<span class="line"><span>│       │   │   ├── dashboard/          # 仪表盘</span></span>
<span class="line"><span>│       │   │   └── wemirr/             # 业务页面</span></span>
<span class="line"><span>│       │   │       ├── system/         # 系统管理</span></span>
<span class="line"><span>│       │   │       │   ├── auth/       # 权限管理</span></span>
<span class="line"><span>│       │   │       │   ├── basic/      # 基础配置</span></span>
<span class="line"><span>│       │   │       │   ├── log/        # 日志管理</span></span>
<span class="line"><span>│       │   │       │   ├── message/    # 消息中心</span></span>
<span class="line"><span>│       │   │       │   ├── monitor/    # 监控中心</span></span>
<span class="line"><span>│       │   │       │   ├── org/        # 组织管理</span></span>
<span class="line"><span>│       │   │       │   ├── oss/        # 文件存储</span></span>
<span class="line"><span>│       │   │       │   ├── position/   # 职位管理</span></span>
<span class="line"><span>│       │   │       │   └── user/       # 用户管理</span></span>
<span class="line"><span>│       │   │       ├── platform/       # 平台管理</span></span>
<span class="line"><span>│       │   │       │   ├── basic/      # 基础配置</span></span>
<span class="line"><span>│       │   │       │   ├── db/         # 数据源</span></span>
<span class="line"><span>│       │   │       │   ├── plan/       # 套餐管理</span></span>
<span class="line"><span>│       │   │       │   ├── security/   # 安全设置</span></span>
<span class="line"><span>│       │   │       │   └── tenant/     # 租户管理</span></span>
<span class="line"><span>│       │   │       ├── develop/        # 开发工具</span></span>
<span class="line"><span>│       │   │       ├── workflow/       # 工作流</span></span>
<span class="line"><span>│       │   │       ├── ai/             # AI 功能</span></span>
<span class="line"><span>│       │   │       ├── tms/            # 运输管理</span></span>
<span class="line"><span>│       │   │       └── wms/            # 仓储管理</span></span>
<span class="line"><span>│       │   │</span></span>
<span class="line"><span>│       │   ├── adapter/                # 适配器</span></span>
<span class="line"><span>│       │   ├── assets/                 # 静态资源</span></span>
<span class="line"><span>│       │   ├── components/             # 🧩 业务组件</span></span>
<span class="line"><span>│       │   ├── layouts/                # 布局组件</span></span>
<span class="line"><span>│       │   ├── locales/                # 国际化</span></span>
<span class="line"><span>│       │   ├── plugin/                 # 插件</span></span>
<span class="line"><span>│       │   ├── router/                 # 路由配置</span></span>
<span class="line"><span>│       │   ├── store/                  # 状态管理</span></span>
<span class="line"><span>│       │   └── utils/                  # 工具函数</span></span>
<span class="line"><span>│       │</span></span>
<span class="line"><span>│       ├── .env                        # 环境变量</span></span>
<span class="line"><span>│       ├── .env.development            # 开发环境</span></span>
<span class="line"><span>│       ├── .env.production             # 生产环境</span></span>
<span class="line"><span>│       └── vite.config.mts             # Vite 配置</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── packages/                           # 📦 共享包</span></span>
<span class="line"><span>│   ├── @core/                          # 核心包</span></span>
<span class="line"><span>│   ├── constants/                      # 常量定义</span></span>
<span class="line"><span>│   ├── effects/                        # 副作用</span></span>
<span class="line"><span>│   ├── hooks/                          # 组合式函数</span></span>
<span class="line"><span>│   ├── icons/                          # 图标</span></span>
<span class="line"><span>│   ├── locales/                        # 国际化</span></span>
<span class="line"><span>│   ├── preferences/                    # 偏好设置</span></span>
<span class="line"><span>│   ├── stores/                         # 状态管理</span></span>
<span class="line"><span>│   ├── styles/                         # 样式</span></span>
<span class="line"><span>│   ├── types/                          # 类型定义</span></span>
<span class="line"><span>│   └── utils/                          # 工具函数</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── internal/                           # 🔧 内部工具</span></span>
<span class="line"><span>├── scripts/                            # 脚本</span></span>
<span class="line"><span>├── pnpm-workspace.yaml                 # Monorepo 配置</span></span>
<span class="line"><span>├── turbo.json                          # Turbo 配置</span></span>
<span class="line"><span>└── package.json                        # 依赖管理</span></span></code></pre>
</div><h3 id="前端技术栈" tabindex="-1">前端技术栈 <a class="header-anchor" href="#前端技术栈" aria-label="Permalink to &quot;前端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vue</td>
<td>3.x</td>
<td>渐进式 JavaScript 框架</td>
</tr>
<tr>
<td>Vben Admin</td>
<td>5.x</td>
<td>后台管理模板（Monorepo 架构）</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.x</td>
<td>UI 组件库</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.x</td>
<td>类型安全</td>
</tr>
<tr>
<td>Vite</td>
<td>5.x</td>
<td>构建工具</td>
</tr>
<tr>
<td>Pinia</td>
<td>2.x</td>
<td>状态管理</td>
</tr>
<tr>
<td>TailwindCSS</td>
<td>3.x</td>
<td>原子化 CSS</td>
</tr>
<tr>
<td>Turbo</td>
<td>-</td>
<td>Monorepo 构建工具</td>
</tr>
</tbody>
</table>
<h2 id="框架组件" tabindex="-1">框架组件 <a class="header-anchor" href="#框架组件" aria-label="Permalink to &quot;框架组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="common-framework-core" tabindex="-1">common-framework-core <a class="header-anchor" href="#common-framework-core" aria-label="Permalink to &quot;common-framework-core&quot;">&ZeroWidthSpace;</a></h3>
<p>基础核心模块，提供基础注解、接口、类给其他 starter 使用。</p>
<h3 id="db-spring-boot-starter" tabindex="-1">db-spring-boot-starter <a class="header-anchor" href="#db-spring-boot-starter" aria-label="Permalink to &quot;db-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>数据库增强模块，集成 MyBatis-Plus 和多租户支持。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>多租户隔离</strong> - 字段隔离 / Schema 隔离 / 数据源隔离</li>
<li><strong>数据权限</strong> - 全部 / 本部门 / 本人</li>
<li><strong>自动填充</strong> - 创建人、创建时间、更新人、更新时间</li>
<li><strong>逻辑删除</strong> - 统一软删除处理</li>
<li><strong>分页插件</strong> - 自动分页处理</li>
</ul>
<h3 id="security-spring-boot-starter" tabindex="-1">security-spring-boot-starter <a class="header-anchor" href="#security-spring-boot-starter" aria-label="Permalink to &quot;security-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>安全认证模块，基于 Sa-Token 封装。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>登录认证</strong> - 账号密码、手机验证码</li>
<li><strong>权限校验</strong> - 角色校验、权限码校验</li>
<li><strong>会话管理</strong> - Token 管理、踢人下线</li>
<li><strong>租户识别</strong> - 自动识别当前租户</li>
</ul>
<h3 id="feign-plugin-spring-boot-starter" tabindex="-1">feign-plugin-spring-boot-starter <a class="header-anchor" href="#feign-plugin-spring-boot-starter" aria-label="Permalink to &quot;feign-plugin-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>Feign 增强模块，简化微服务调用。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>Token 传递</strong> - 自动传递认证信息</li>
<li><strong>Header 复制</strong> - 复制请求头到下游</li>
<li><strong>服务代理</strong> - 简化 Feign 调用方式</li>
<li><strong>熔断降级</strong> - 集成 Sentinel</li>
</ul>
<h3 id="websocket-spring-boot-starter" tabindex="-1">websocket-spring-boot-starter <a class="header-anchor" href="#websocket-spring-boot-starter" aria-label="Permalink to &quot;websocket-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>分布式 WebSocket 模块，基于 Redis 实现。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>分布式消息</strong> - 多节点消息同步</li>
<li><strong>用户推送</strong> - 指定用户推送</li>
<li><strong>广播推送</strong> - 全体用户推送</li>
<li><strong>租户隔离</strong> - 租户级别消息隔离</li>
</ul>
<h3 id="robot-spring-boot-starter" tabindex="-1">robot-spring-boot-starter <a class="header-anchor" href="#robot-spring-boot-starter" aria-label="Permalink to &quot;robot-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>消息机器人模块，支持多平台推送。</p>
<p><strong>支持平台</strong>：</p>
<ul>
<li><strong>钉钉</strong> - 钉钉机器人</li>
<li><strong>企业微信</strong> - 企微机器人</li>
<li><strong>飞书</strong> - 飞书机器人</li>
</ul>
<h2 id="配套资源" tabindex="-1">配套资源 <a class="header-anchor" href="#配套资源" aria-label="Permalink to &quot;配套资源&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="附件目录说明" tabindex="-1">附件目录说明 <a class="header-anchor" href="#附件目录说明" aria-label="Permalink to &quot;附件目录说明&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>附件/</span></span>
<span class="line"><span>├── docker/                   # Docker 编排文件</span></span>
<span class="line"><span>├── mysql/                    # 数据库脚本</span></span>
<span class="line"><span>├── nacos/                    # Nacos 配置文件</span></span>
<span class="line"><span>└── nginx/                    # Nginx 配置</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/guide/quickstart/">快速上手</a> - 5分钟启动项目</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 容器化部署指南</li>
<li><a href="/zh/guide/frontend/">前端开发</a> - 前端开发指南</li>
<li><a href="/zh/guide/backend/">后端开发</a> - 后端开发指南</li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="项目结构" tabindex="-1">项目结构 <a class="header-anchor" href="#项目结构" aria-label="Permalink to &quot;项目结构&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">源码地址</p>
<ul>
<li><strong>后端仓库</strong>：<a href="https://gitee.com/battcn/wemirr-platform" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform</a> （分支：v4-dev）</li>
<li><strong>前端仓库</strong>：<a href="https://gitee.com/battcn/wemirr-platform-ui" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform-ui</a> （分支：v4-dev）</li>
</ul>
</div>
<h2 id="后端项目结构" tabindex="-1">后端项目结构 <a class="header-anchor" href="#后端项目结构" aria-label="Permalink to &quot;后端项目结构&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>wemirr-platform</strong> 采用 Maven 多模块架构，基于 Spring Cloud Alibaba 2023 构建。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform/</span></span>
<span class="line"><span>├── wemirr-platform-dependencies/       # 📦 依赖版本管理（BOM）</span></span>
<span class="line"><span>├── wemirr-platform-framework/          # 🔧 框架核心层</span></span>
<span class="line"><span>│   ├── common-framework-core/          # 基础核心（注解、接口、工具类）</span></span>
<span class="line"><span>│   ├── common-spring-boot-starter/     # Spring Boot 核心启动器</span></span>
<span class="line"><span>│   ├── db-spring-boot-starter/         # 数据库增强（MyBatis-Plus、多租户、数据权限）</span></span>
<span class="line"><span>│   ├── redis-plus-spring-boot-starter/ # Redis 缓存增强</span></span>
<span class="line"><span>│   ├── security-spring-boot-starter/   # 安全认证（Sa-Token）</span></span>
<span class="line"><span>│   ├── feign-plugin-spring-boot-starter/ # Feign 增强（Token传递、服务代理）</span></span>
<span class="line"><span>│   ├── easyexcel-spring-boot-starter/  # Excel 导入导出</span></span>
<span class="line"><span>│   ├── diff-log-spring-boot-starter/   # 差异日志</span></span>
<span class="line"><span>│   ├── i18n-spring-boot-starter/       # 国际化</span></span>
<span class="line"><span>│   ├── mongodb-plus-spring-boot-starter/ # MongoDB 增强</span></span>
<span class="line"><span>│   ├── pdf-spring-boot-starter/        # PDF 生成</span></span>
<span class="line"><span>│   ├── robot-spring-boot-starter/      # 消息机器人（钉钉、企微、飞书）</span></span>
<span class="line"><span>│   └── websocket-spring-boot-starter/  # 分布式 WebSocket</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-feign/              # 🔗 Feign 接口定义</span></span>
<span class="line"><span>│   ├── wemirr-platform-iam-api/        # IAM 服务 API</span></span>
<span class="line"><span>│   ├── wemirr-platform-suite-api/      # Suite 服务 API</span></span>
<span class="line"><span>│   └── wemirr-platform-workflow-api/   # 工作流服务 API</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-iam/                # 🔐 IAM 认证授权中心</span></span>
<span class="line"><span>│   └── src/main/java/.../iam/</span></span>
<span class="line"><span>│       ├── auth/                       # 认证模块</span></span>
<span class="line"><span>│       ├── base/                       # 基础模块</span></span>
<span class="line"><span>│       ├── system/                     # 系统管理</span></span>
<span class="line"><span>│       │   ├── controller/             # 控制器</span></span>
<span class="line"><span>│       │   ├── domain/                 # 领域模型</span></span>
<span class="line"><span>│       │   ├── repository/             # 数据访问</span></span>
<span class="line"><span>│       │   ├── service/                # 业务逻辑</span></span>
<span class="line"><span>│       │   └── func/                   # 功能函数</span></span>
<span class="line"><span>│       └── tenant/                     # 租户管理</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-suite/              # 💼 业务套件中心</span></span>
<span class="line"><span>│   └── src/main/java/.../suite/        # 业务功能实现</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-plugin/                      # 🔌 插件扩展中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-ai/             # AI 能力（Langchain4j）</span></span>
<span class="line"><span>│   ├── wemirr-platform-workflow/       # 审批流程（Warm-Flow）</span></span>
<span class="line"><span>│   ├── wemirr-platform-monitor/        # 监控中心</span></span>
<span class="line"><span>│   ├── wemirr-platform-tms/            # 运输管理（TMS）</span></span>
<span class="line"><span>│   └── wemirr-platform-wms/            # 仓储管理（WMS）</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── wemirr-platform-gateway/            # 🚪 API 网关</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── dev-support/                        # 🛠️ 开发支持</span></span>
<span class="line"><span>├── docs/                               # 📚 文档</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>└── 附件/                                # 📁 附件资源</span></span>
<span class="line"><span>    ├── docker/                         # Docker 编排文件</span></span>
<span class="line"><span>    ├── mysql/                          # 数据库脚本</span></span>
<span class="line"><span>    ├── nacos/                          # Nacos 配置文件</span></span>
<span class="line"><span>    └── nginx/                          # Nginx 配置</span></span></code></pre>
</div><h3 id="核心模块说明" tabindex="-1">核心模块说明 <a class="header-anchor" href="#核心模块说明" aria-label="Permalink to &quot;核心模块说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>模块</th>
<th>说明</th>
<th>技术栈</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>wemirr-platform-iam</strong></td>
<td>IAM 认证授权中心，用户、角色、菜单、租户管理</td>
<td>Sa-Token、MyBatis-Plus</td>
</tr>
<tr>
<td><strong>wemirr-platform-suite</strong></td>
<td>业务套件，存放核心业务功能</td>
<td>Spring Boot</td>
</tr>
<tr>
<td><strong>wemirr-plugin</strong></td>
<td>插件中心，可插拔的扩展功能（AI、工作流、WMS、TMS）</td>
<td>Warm-Flow、Langchain4j</td>
</tr>
<tr>
<td><strong>wemirr-platform-gateway</strong></td>
<td>API 网关，统一入口、限流、鉴权</td>
<td>Spring Cloud Gateway</td>
</tr>
<tr>
<td><strong>wemirr-platform-framework</strong></td>
<td>框架层，提供通用能力封装</td>
<td>多个 Spring Boot Starter</td>
</tr>
<tr>
<td><strong>wemirr-platform-feign</strong></td>
<td>Feign 接口定义，服务间调用</td>
<td>OpenFeign</td>
</tr>
</tbody>
</table>
<h2 id="前端项目结构" tabindex="-1">前端项目结构 <a class="header-anchor" href="#前端项目结构" aria-label="Permalink to &quot;前端项目结构&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>wemirr-platform-ui</strong> 基于 <strong>Vben Admin 5.x</strong> + Vue 3 + Ant Design Vue 构建，采用 <strong>Monorepo</strong> 架构。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform-ui/</span></span>
<span class="line"><span>├── apps/                               # 📱 应用目录</span></span>
<span class="line"><span>│   └── web-antd/                       # Ant Design Vue 版本</span></span>
<span class="line"><span>│       ├── src/</span></span>
<span class="line"><span>│       │   ├── api/                    # 🌐 API 接口</span></span>
<span class="line"><span>│       │   │   ├── core/               # 核心接口</span></span>
<span class="line"><span>│       │   │   ├── system/             # 系统接口</span></span>
<span class="line"><span>│       │   │   │   ├── oss/            # 文件存储</span></span>
<span class="line"><span>│       │   │   │   └── user/           # 用户管理</span></span>
<span class="line"><span>│       │   │   └── workflow/           # 工作流接口</span></span>
<span class="line"><span>│       │   │</span></span>
<span class="line"><span>│       │   ├── views/                  # 📄 页面视图</span></span>
<span class="line"><span>│       │   │   ├── _core/              # 核心页面（登录等）</span></span>
<span class="line"><span>│       │   │   ├── dashboard/          # 仪表盘</span></span>
<span class="line"><span>│       │   │   └── wemirr/             # 业务页面</span></span>
<span class="line"><span>│       │   │       ├── system/         # 系统管理</span></span>
<span class="line"><span>│       │   │       │   ├── auth/       # 权限管理</span></span>
<span class="line"><span>│       │   │       │   ├── basic/      # 基础配置</span></span>
<span class="line"><span>│       │   │       │   ├── log/        # 日志管理</span></span>
<span class="line"><span>│       │   │       │   ├── message/    # 消息中心</span></span>
<span class="line"><span>│       │   │       │   ├── monitor/    # 监控中心</span></span>
<span class="line"><span>│       │   │       │   ├── org/        # 组织管理</span></span>
<span class="line"><span>│       │   │       │   ├── oss/        # 文件存储</span></span>
<span class="line"><span>│       │   │       │   ├── position/   # 职位管理</span></span>
<span class="line"><span>│       │   │       │   └── user/       # 用户管理</span></span>
<span class="line"><span>│       │   │       ├── platform/       # 平台管理</span></span>
<span class="line"><span>│       │   │       │   ├── basic/      # 基础配置</span></span>
<span class="line"><span>│       │   │       │   ├── db/         # 数据源</span></span>
<span class="line"><span>│       │   │       │   ├── plan/       # 套餐管理</span></span>
<span class="line"><span>│       │   │       │   ├── security/   # 安全设置</span></span>
<span class="line"><span>│       │   │       │   └── tenant/     # 租户管理</span></span>
<span class="line"><span>│       │   │       ├── develop/        # 开发工具</span></span>
<span class="line"><span>│       │   │       ├── workflow/       # 工作流</span></span>
<span class="line"><span>│       │   │       ├── ai/             # AI 功能</span></span>
<span class="line"><span>│       │   │       ├── tms/            # 运输管理</span></span>
<span class="line"><span>│       │   │       └── wms/            # 仓储管理</span></span>
<span class="line"><span>│       │   │</span></span>
<span class="line"><span>│       │   ├── adapter/                # 适配器</span></span>
<span class="line"><span>│       │   ├── assets/                 # 静态资源</span></span>
<span class="line"><span>│       │   ├── components/             # 🧩 业务组件</span></span>
<span class="line"><span>│       │   ├── layouts/                # 布局组件</span></span>
<span class="line"><span>│       │   ├── locales/                # 国际化</span></span>
<span class="line"><span>│       │   ├── plugin/                 # 插件</span></span>
<span class="line"><span>│       │   ├── router/                 # 路由配置</span></span>
<span class="line"><span>│       │   ├── store/                  # 状态管理</span></span>
<span class="line"><span>│       │   └── utils/                  # 工具函数</span></span>
<span class="line"><span>│       │</span></span>
<span class="line"><span>│       ├── .env                        # 环境变量</span></span>
<span class="line"><span>│       ├── .env.development            # 开发环境</span></span>
<span class="line"><span>│       ├── .env.production             # 生产环境</span></span>
<span class="line"><span>│       └── vite.config.mts             # Vite 配置</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── packages/                           # 📦 共享包</span></span>
<span class="line"><span>│   ├── @core/                          # 核心包</span></span>
<span class="line"><span>│   ├── constants/                      # 常量定义</span></span>
<span class="line"><span>│   ├── effects/                        # 副作用</span></span>
<span class="line"><span>│   ├── hooks/                          # 组合式函数</span></span>
<span class="line"><span>│   ├── icons/                          # 图标</span></span>
<span class="line"><span>│   ├── locales/                        # 国际化</span></span>
<span class="line"><span>│   ├── preferences/                    # 偏好设置</span></span>
<span class="line"><span>│   ├── stores/                         # 状态管理</span></span>
<span class="line"><span>│   ├── styles/                         # 样式</span></span>
<span class="line"><span>│   ├── types/                          # 类型定义</span></span>
<span class="line"><span>│   └── utils/                          # 工具函数</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── internal/                           # 🔧 内部工具</span></span>
<span class="line"><span>├── scripts/                            # 脚本</span></span>
<span class="line"><span>├── pnpm-workspace.yaml                 # Monorepo 配置</span></span>
<span class="line"><span>├── turbo.json                          # Turbo 配置</span></span>
<span class="line"><span>└── package.json                        # 依赖管理</span></span></code></pre>
</div><h3 id="前端技术栈" tabindex="-1">前端技术栈 <a class="header-anchor" href="#前端技术栈" aria-label="Permalink to &quot;前端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vue</td>
<td>3.x</td>
<td>渐进式 JavaScript 框架</td>
</tr>
<tr>
<td>Vben Admin</td>
<td>5.x</td>
<td>后台管理模板（Monorepo 架构）</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.x</td>
<td>UI 组件库</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.x</td>
<td>类型安全</td>
</tr>
<tr>
<td>Vite</td>
<td>5.x</td>
<td>构建工具</td>
</tr>
<tr>
<td>Pinia</td>
<td>2.x</td>
<td>状态管理</td>
</tr>
<tr>
<td>TailwindCSS</td>
<td>3.x</td>
<td>原子化 CSS</td>
</tr>
<tr>
<td>Turbo</td>
<td>-</td>
<td>Monorepo 构建工具</td>
</tr>
</tbody>
</table>
<h2 id="框架组件" tabindex="-1">框架组件 <a class="header-anchor" href="#框架组件" aria-label="Permalink to &quot;框架组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="common-framework-core" tabindex="-1">common-framework-core <a class="header-anchor" href="#common-framework-core" aria-label="Permalink to &quot;common-framework-core&quot;">&ZeroWidthSpace;</a></h3>
<p>基础核心模块，提供基础注解、接口、类给其他 starter 使用。</p>
<h3 id="db-spring-boot-starter" tabindex="-1">db-spring-boot-starter <a class="header-anchor" href="#db-spring-boot-starter" aria-label="Permalink to &quot;db-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>数据库增强模块，集成 MyBatis-Plus 和多租户支持。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>多租户隔离</strong> - 字段隔离 / Schema 隔离 / 数据源隔离</li>
<li><strong>数据权限</strong> - 全部 / 本部门 / 本人</li>
<li><strong>自动填充</strong> - 创建人、创建时间、更新人、更新时间</li>
<li><strong>逻辑删除</strong> - 统一软删除处理</li>
<li><strong>分页插件</strong> - 自动分页处理</li>
</ul>
<h3 id="security-spring-boot-starter" tabindex="-1">security-spring-boot-starter <a class="header-anchor" href="#security-spring-boot-starter" aria-label="Permalink to &quot;security-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>安全认证模块，基于 Sa-Token 封装。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>登录认证</strong> - 账号密码、手机验证码</li>
<li><strong>权限校验</strong> - 角色校验、权限码校验</li>
<li><strong>会话管理</strong> - Token 管理、踢人下线</li>
<li><strong>租户识别</strong> - 自动识别当前租户</li>
</ul>
<h3 id="feign-plugin-spring-boot-starter" tabindex="-1">feign-plugin-spring-boot-starter <a class="header-anchor" href="#feign-plugin-spring-boot-starter" aria-label="Permalink to &quot;feign-plugin-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>Feign 增强模块，简化微服务调用。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>Token 传递</strong> - 自动传递认证信息</li>
<li><strong>Header 复制</strong> - 复制请求头到下游</li>
<li><strong>服务代理</strong> - 简化 Feign 调用方式</li>
<li><strong>熔断降级</strong> - 集成 Sentinel</li>
</ul>
<h3 id="websocket-spring-boot-starter" tabindex="-1">websocket-spring-boot-starter <a class="header-anchor" href="#websocket-spring-boot-starter" aria-label="Permalink to &quot;websocket-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>分布式 WebSocket 模块，基于 Redis 实现。</p>
<p><strong>核心功能</strong>：</p>
<ul>
<li><strong>分布式消息</strong> - 多节点消息同步</li>
<li><strong>用户推送</strong> - 指定用户推送</li>
<li><strong>广播推送</strong> - 全体用户推送</li>
<li><strong>租户隔离</strong> - 租户级别消息隔离</li>
</ul>
<h3 id="robot-spring-boot-starter" tabindex="-1">robot-spring-boot-starter <a class="header-anchor" href="#robot-spring-boot-starter" aria-label="Permalink to &quot;robot-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h3>
<p>消息机器人模块，支持多平台推送。</p>
<p><strong>支持平台</strong>：</p>
<ul>
<li><strong>钉钉</strong> - 钉钉机器人</li>
<li><strong>企业微信</strong> - 企微机器人</li>
<li><strong>飞书</strong> - 飞书机器人</li>
</ul>
<h2 id="配套资源" tabindex="-1">配套资源 <a class="header-anchor" href="#配套资源" aria-label="Permalink to &quot;配套资源&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="附件目录说明" tabindex="-1">附件目录说明 <a class="header-anchor" href="#附件目录说明" aria-label="Permalink to &quot;附件目录说明&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>附件/</span></span>
<span class="line"><span>├── docker/                   # Docker 编排文件</span></span>
<span class="line"><span>├── mysql/                    # 数据库脚本</span></span>
<span class="line"><span>├── nacos/                    # Nacos 配置文件</span></span>
<span class="line"><span>└── nginx/                    # Nginx 配置</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/guide/quickstart/">快速上手</a> - 5分钟启动项目</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 容器化部署指南</li>
<li><a href="/zh/guide/frontend/">前端开发</a> - 前端开发指南</li>
<li><a href="/zh/guide/backend/">后端开发</a> - 后端开发指南</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Docker Compose 一键部署 ]]></title>
            <link>https://docs.battcn.com/zh/guide/docker/compose.html</link>
            <guid>https://docs.battcn.com/zh/guide/docker/compose.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="docker-compose-一键部署" tabindex="-1">Docker Compose 一键部署 <a class="header-anchor" href="#docker-compose-一键部署" aria-label="Permalink to &quot;Docker Compose 一键部署&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>使用 Docker Compose 一键部署 Wemirr Platform 所需的全部中间件环境</p>
</div>
<h2 id="什么是-docker-compose" tabindex="-1">什么是 Docker Compose？ <a class="header-anchor" href="#什么是-docker-compose" aria-label="Permalink to &quot;什么是 Docker Compose？&quot;">&ZeroWidthSpace;</a></h2>
<p>Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过一个 YAML 文件配置所有服务，然后一条命令即可创建并启动所有服务。</p>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_0-先准备-docker-网络-必做" tabindex="-1">0. 先准备 Docker 网络（必做） <a class="header-anchor" href="#_0-先准备-docker-网络-必做" aria-label="Permalink to &quot;0. 先准备 Docker 网络（必做）&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr 仓库提供的 <code>docker-compose.yml</code> 默认使用外部网络 <code>wemirr</code>，因此需要提前创建：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><h3 id="_1-创建项目目录" tabindex="-1">1. 创建项目目录 <a class="header-anchor" href="#_1-创建项目目录" aria-label="Permalink to &quot;1. 创建项目目录&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ~/wemirr/docker</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ~/wemirr/docker</span></span></code></pre>
</div><h3 id="_2-直接使用仓库自带-docker-compose-yml-推荐" tabindex="-1">2. 直接使用仓库自带 <code>docker-compose.yml</code>（推荐） <a class="header-anchor" href="#_2-直接使用仓库自带-docker-compose-yml-推荐" aria-label="Permalink to &quot;2. 直接使用仓库自带 `docker-compose.yml`（推荐）&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr 后端仓库已经提供了可用的中间件编排文件（并且与项目配置保持一致）：</p>
<ul>
<li>文件路径：<code>wemirr-platform/附件/docker/docker-compose.yml</code></li>
</ul>
<p>你可以直接复制到本机某个目录运行，也可以在仓库目录直接运行。</p>
<h4 id="版本对齐-非常重要" tabindex="-1">版本对齐（非常重要） <a class="header-anchor" href="#版本对齐-非常重要" aria-label="Permalink to &quot;版本对齐（非常重要）&quot;">&ZeroWidthSpace;</a></h4>
<p>该编排文件当前使用的镜像版本：</p>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>镜像</th>
<th>版本</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td><code>mysql</code></td>
<td><code>8.0.19</code></td>
</tr>
<tr>
<td>Redis</td>
<td><code>redis</code></td>
<td><code>5.0</code></td>
</tr>
<tr>
<td>Nacos</td>
<td><code>nacos/nacos-server</code></td>
<td><code>2.4.2</code></td>
</tr>
<tr>
<td>RabbitMQ</td>
<td><code>docker.io/macintoshplus/rabbitmq-management</code></td>
<td>镜像未固定 tag（以仓库为准）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>文档后续所有示例均以此为准，避免出现“文档能跑，代码跑不了 / 代码能跑，文档版本不对”的问题。</p>
</blockquote>
<h3 id="_3-启动服务" tabindex="-1">3. 启动服务 <a class="header-anchor" href="#_3-启动服务" aria-label="Permalink to &quot;3. 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动所有服务（后台运行）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看服务状态</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看指定服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><h3 id="_4-初始化数据与配置" tabindex="-1">4. 初始化数据与配置 <a class="header-anchor" href="#_4-初始化数据与配置" aria-label="Permalink to &quot;4. 初始化数据与配置&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>数据库初始化脚本</strong>：<code>wemirr-platform/附件/mysql/</code></li>
<li><strong>Nacos 配置包</strong>：<code>wemirr-platform/附件/nacos/</code></li>
</ul>
<blockquote>
<p>数据库初始化与 Nacos 导入在 <a href="/zh/guide/quickstart/">快速上手</a> 已给出操作步骤，此处不重复。</p>
</blockquote>
<h3 id="_5-验证服务" tabindex="-1">5. 验证服务 <a class="header-anchor" href="#_5-验证服务" aria-label="Permalink to &quot;5. 验证服务&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>服务</th>
<th>地址</th>
<th>账号/密码</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td>localhost:3306</td>
<td>root / 123456</td>
</tr>
<tr>
<td>Redis</td>
<td>localhost:6379</td>
<td>默认无密码（以 <code>附件/docker/docker-compose.yml</code> 为准）</td>
</tr>
<tr>
<td>Nacos</td>
<td><a href="http://localhost:8848/nacos" target="_blank" rel="noreferrer">http://localhost:8848/nacos</a></td>
<td>nacos / nacos</td>
</tr>
<tr>
<td>RabbitMQ</td>
<td><a href="http://localhost:15672" target="_blank" rel="noreferrer">http://localhost:15672</a></td>
<td>镜像默认账号密码（以镜像说明为准）</td>
</tr>
</tbody>
</table>
<h2 id="常用命令" tabindex="-1">常用命令 <a class="header-anchor" href="#常用命令" aria-label="Permalink to &quot;常用命令&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="服务管理" tabindex="-1">服务管理 <a class="header-anchor" href="#服务管理" aria-label="Permalink to &quot;服务管理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> stop</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止并删除所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> down</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止并删除服务+数据卷（危险！会删除数据）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> down</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重启所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> restart</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重启指定服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重新构建并启动</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --build</span></span></code></pre>
</div><h3 id="端口与健康检查-建议你自己先核对" tabindex="-1">端口与健康检查（建议你自己先核对） <a class="header-anchor" href="#端口与健康检查-建议你自己先核对" aria-label="Permalink to &quot;端口与健康检查（建议你自己先核对）&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>端口</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td>3306</td>
<td>root/123456</td>
</tr>
<tr>
<td>Redis</td>
<td>6379</td>
<td>默认无密码（以仓库 compose 为准）</td>
</tr>
<tr>
<td>Nacos</td>
<td>8848/9848/9849</td>
<td>控制台/GRPC</td>
</tr>
<tr>
<td>RabbitMQ</td>
<td>5671/5672/15672/25672</td>
<td>AMQP/管理台</td>
</tr>
</tbody>
</table>
<h3 id="日志查看" tabindex="-1">日志查看 <a class="header-anchor" href="#日志查看" aria-label="Permalink to &quot;日志查看&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看所有服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 实时查看日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看指定服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看最近100行日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --tail=100</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span></code></pre>
</div><h3 id="服务扩缩容" tabindex="-1">服务扩缩容 <a class="header-anchor" href="#服务扩缩容" aria-label="Permalink to &quot;服务扩缩容&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 扩展服务实例数</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --scale</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> service-name=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span></span></code></pre>
</div><h2 id="开发环境配置" tabindex="-1">开发环境配置 <a class="header-anchor" href="#开发环境配置" aria-label="Permalink to &quot;开发环境配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="最小配置-必需" tabindex="-1">最小配置（必需） <a class="header-anchor" href="#最小配置-必需" aria-label="Permalink to &quot;最小配置（必需）&quot;">&ZeroWidthSpace;</a></h3>
<p>仅包含必需的中间件：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'3.8'</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  wemirr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    driver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">bridge</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql:8.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-mysql</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"3306:3306"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_ROOT_PASSWORD=123456</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis:7-alpine</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"6379:6379"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos/nacos-server:v2.3.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-nacos</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8848:8848"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9848:9848"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MODE=standalone</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span></code></pre>
</div><h3 id="完整配置-推荐" tabindex="-1">完整配置（推荐） <a class="header-anchor" href="#完整配置-推荐" aria-label="Permalink to &quot;完整配置（推荐）&quot;">&ZeroWidthSpace;</a></h3>
<p>包含所有可选中间件，参考上面的完整 <code>docker-compose.yml</code>。</p>
<h2 id="环境变量配置" tabindex="-1">环境变量配置 <a class="header-anchor" href="#环境变量配置" aria-label="Permalink to &quot;环境变量配置&quot;">&ZeroWidthSpace;</a></h2>
<p>创建 <code>.env</code> 文件管理环境变量：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># .env</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_password</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">REDIS_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_redis_password</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_AUTH_TOKEN</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_token_at_least_32_chars</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_USER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio_admin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio_secure_password</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">RABBITMQ_USER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">rabbit_admin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">RABBITMQ_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">rabbit_secure_password</span></span></code></pre>
</div><p>在 <code>docker-compose.yml</code> 中引用：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}</span></span></code></pre>
</div><h2 id="数据持久化" tabindex="-1">数据持久化 <a class="header-anchor" href="#数据持久化" aria-label="Permalink to &quot;数据持久化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="使用命名卷-推荐" tabindex="-1">使用命名卷（推荐） <a class="header-anchor" href="#使用命名卷-推荐" aria-label="Permalink to &quot;使用命名卷（推荐）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql-data:/var/lib/mysql</span></span></code></pre>
</div><div class="warning custom-block"><p class="custom-block-title">仓库自带 compose 目前未挂载 Volume</p>
<p><code>wemirr-platform/附件/docker/docker-compose.yml</code> 以“开箱即用”为主，默认未挂载数据卷。</p>
<p>如果你用于长期开发环境，建议你在本地 copy 一份 compose 并增加 volume 挂载，避免容器删除导致数据丢失。</p>
</div>
<h3 id="使用本地目录" tabindex="-1">使用本地目录 <a class="header-anchor" href="#使用本地目录" aria-label="Permalink to &quot;使用本地目录&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./data/mysql:/var/lib/mysql</span></span></code></pre>
</div><h2 id="故障排查" tabindex="-1">故障排查 <a class="header-anchor" href="#故障排查" aria-label="Permalink to &quot;故障排查&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="服务启动失败" tabindex="-1">服务启动失败 <a class="header-anchor" href="#服务启动失败" aria-label="Permalink to &quot;服务启动失败&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看失败原因</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> service-name</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查容器状态</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -a</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 进入容器调试</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> service-name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sh</span></span></code></pre>
</div><h3 id="经典错误-network-wemirr-not-found" tabindex="-1">经典错误：<code>network wemirr not found</code> <a class="header-anchor" href="#经典错误-network-wemirr-not-found" aria-label="Permalink to &quot;经典错误：`network wemirr not found`&quot;">&ZeroWidthSpace;</a></h3>
<p>原因：未创建外部网络 <code>wemirr</code>。</p>
<p>解决：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span></code></pre>
</div><h3 id="nacos-启动失败" tabindex="-1">Nacos 启动失败 <a class="header-anchor" href="#nacos-启动失败" aria-label="Permalink to &quot;Nacos 启动失败&quot;">&ZeroWidthSpace;</a></h3>
<p>常见原因：MySQL 未就绪</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 先单独启动 MySQL</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 等待 MySQL 就绪后再启动 Nacos</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><h3 id="端口冲突" tabindex="-1">端口冲突 <a class="header-anchor" href="#端口冲突" aria-label="Permalink to &quot;端口冲突&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:3306</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:8848</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 修改 docker-compose.yml 中的端口映射</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "13306:3306"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 改为 13306</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/docker/production.html">生产环境部署</a> - 学习生产级别的部署配置</li>
<li><a href="/zh/guide/env/deploy.html">环境部署</a> - 部署后端和前端服务</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="docker-compose-一键部署" tabindex="-1">Docker Compose 一键部署 <a class="header-anchor" href="#docker-compose-一键部署" aria-label="Permalink to &quot;Docker Compose 一键部署&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>使用 Docker Compose 一键部署 Wemirr Platform 所需的全部中间件环境</p>
</div>
<h2 id="什么是-docker-compose" tabindex="-1">什么是 Docker Compose？ <a class="header-anchor" href="#什么是-docker-compose" aria-label="Permalink to &quot;什么是 Docker Compose？&quot;">&ZeroWidthSpace;</a></h2>
<p>Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过一个 YAML 文件配置所有服务，然后一条命令即可创建并启动所有服务。</p>
<h2 id="快速开始" tabindex="-1">快速开始 <a class="header-anchor" href="#快速开始" aria-label="Permalink to &quot;快速开始&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_0-先准备-docker-网络-必做" tabindex="-1">0. 先准备 Docker 网络（必做） <a class="header-anchor" href="#_0-先准备-docker-网络-必做" aria-label="Permalink to &quot;0. 先准备 Docker 网络（必做）&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr 仓库提供的 <code>docker-compose.yml</code> 默认使用外部网络 <code>wemirr</code>，因此需要提前创建：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><h3 id="_1-创建项目目录" tabindex="-1">1. 创建项目目录 <a class="header-anchor" href="#_1-创建项目目录" aria-label="Permalink to &quot;1. 创建项目目录&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ~/wemirr/docker</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ~/wemirr/docker</span></span></code></pre>
</div><h3 id="_2-直接使用仓库自带-docker-compose-yml-推荐" tabindex="-1">2. 直接使用仓库自带 <code>docker-compose.yml</code>（推荐） <a class="header-anchor" href="#_2-直接使用仓库自带-docker-compose-yml-推荐" aria-label="Permalink to &quot;2. 直接使用仓库自带 `docker-compose.yml`（推荐）&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr 后端仓库已经提供了可用的中间件编排文件（并且与项目配置保持一致）：</p>
<ul>
<li>文件路径：<code>wemirr-platform/附件/docker/docker-compose.yml</code></li>
</ul>
<p>你可以直接复制到本机某个目录运行，也可以在仓库目录直接运行。</p>
<h4 id="版本对齐-非常重要" tabindex="-1">版本对齐（非常重要） <a class="header-anchor" href="#版本对齐-非常重要" aria-label="Permalink to &quot;版本对齐（非常重要）&quot;">&ZeroWidthSpace;</a></h4>
<p>该编排文件当前使用的镜像版本：</p>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>镜像</th>
<th>版本</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td><code>mysql</code></td>
<td><code>8.0.19</code></td>
</tr>
<tr>
<td>Redis</td>
<td><code>redis</code></td>
<td><code>5.0</code></td>
</tr>
<tr>
<td>Nacos</td>
<td><code>nacos/nacos-server</code></td>
<td><code>2.4.2</code></td>
</tr>
<tr>
<td>RabbitMQ</td>
<td><code>docker.io/macintoshplus/rabbitmq-management</code></td>
<td>镜像未固定 tag（以仓库为准）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>文档后续所有示例均以此为准，避免出现“文档能跑，代码跑不了 / 代码能跑，文档版本不对”的问题。</p>
</blockquote>
<h3 id="_3-启动服务" tabindex="-1">3. 启动服务 <a class="header-anchor" href="#_3-启动服务" aria-label="Permalink to &quot;3. 启动服务&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动所有服务（后台运行）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看服务状态</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看指定服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><h3 id="_4-初始化数据与配置" tabindex="-1">4. 初始化数据与配置 <a class="header-anchor" href="#_4-初始化数据与配置" aria-label="Permalink to &quot;4. 初始化数据与配置&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>数据库初始化脚本</strong>：<code>wemirr-platform/附件/mysql/</code></li>
<li><strong>Nacos 配置包</strong>：<code>wemirr-platform/附件/nacos/</code></li>
</ul>
<blockquote>
<p>数据库初始化与 Nacos 导入在 <a href="/zh/guide/quickstart/">快速上手</a> 已给出操作步骤，此处不重复。</p>
</blockquote>
<h3 id="_5-验证服务" tabindex="-1">5. 验证服务 <a class="header-anchor" href="#_5-验证服务" aria-label="Permalink to &quot;5. 验证服务&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>服务</th>
<th>地址</th>
<th>账号/密码</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td>localhost:3306</td>
<td>root / 123456</td>
</tr>
<tr>
<td>Redis</td>
<td>localhost:6379</td>
<td>默认无密码（以 <code>附件/docker/docker-compose.yml</code> 为准）</td>
</tr>
<tr>
<td>Nacos</td>
<td><a href="http://localhost:8848/nacos" target="_blank" rel="noreferrer">http://localhost:8848/nacos</a></td>
<td>nacos / nacos</td>
</tr>
<tr>
<td>RabbitMQ</td>
<td><a href="http://localhost:15672" target="_blank" rel="noreferrer">http://localhost:15672</a></td>
<td>镜像默认账号密码（以镜像说明为准）</td>
</tr>
</tbody>
</table>
<h2 id="常用命令" tabindex="-1">常用命令 <a class="header-anchor" href="#常用命令" aria-label="Permalink to &quot;常用命令&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="服务管理" tabindex="-1">服务管理 <a class="header-anchor" href="#服务管理" aria-label="Permalink to &quot;服务管理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> stop</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止并删除所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> down</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止并删除服务+数据卷（危险！会删除数据）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> down</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重启所有服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> restart</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重启指定服务</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重新构建并启动</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --build</span></span></code></pre>
</div><h3 id="端口与健康检查-建议你自己先核对" tabindex="-1">端口与健康检查（建议你自己先核对） <a class="header-anchor" href="#端口与健康检查-建议你自己先核对" aria-label="Permalink to &quot;端口与健康检查（建议你自己先核对）&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>端口</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td>3306</td>
<td>root/123456</td>
</tr>
<tr>
<td>Redis</td>
<td>6379</td>
<td>默认无密码（以仓库 compose 为准）</td>
</tr>
<tr>
<td>Nacos</td>
<td>8848/9848/9849</td>
<td>控制台/GRPC</td>
</tr>
<tr>
<td>RabbitMQ</td>
<td>5671/5672/15672/25672</td>
<td>AMQP/管理台</td>
</tr>
</tbody>
</table>
<h3 id="日志查看" tabindex="-1">日志查看 <a class="header-anchor" href="#日志查看" aria-label="Permalink to &quot;日志查看&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看所有服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 实时查看日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看指定服务日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看最近100行日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --tail=100</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span></code></pre>
</div><h3 id="服务扩缩容" tabindex="-1">服务扩缩容 <a class="header-anchor" href="#服务扩缩容" aria-label="Permalink to &quot;服务扩缩容&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 扩展服务实例数</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --scale</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> service-name=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span></span></code></pre>
</div><h2 id="开发环境配置" tabindex="-1">开发环境配置 <a class="header-anchor" href="#开发环境配置" aria-label="Permalink to &quot;开发环境配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="最小配置-必需" tabindex="-1">最小配置（必需） <a class="header-anchor" href="#最小配置-必需" aria-label="Permalink to &quot;最小配置（必需）&quot;">&ZeroWidthSpace;</a></h3>
<p>仅包含必需的中间件：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'3.8'</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  wemirr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    driver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">bridge</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql:8.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-mysql</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"3306:3306"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_ROOT_PASSWORD=123456</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis:7-alpine</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"6379:6379"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos/nacos-server:v2.3.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-nacos</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8848:8848"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9848:9848"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MODE=standalone</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span></code></pre>
</div><h3 id="完整配置-推荐" tabindex="-1">完整配置（推荐） <a class="header-anchor" href="#完整配置-推荐" aria-label="Permalink to &quot;完整配置（推荐）&quot;">&ZeroWidthSpace;</a></h3>
<p>包含所有可选中间件，参考上面的完整 <code>docker-compose.yml</code>。</p>
<h2 id="环境变量配置" tabindex="-1">环境变量配置 <a class="header-anchor" href="#环境变量配置" aria-label="Permalink to &quot;环境变量配置&quot;">&ZeroWidthSpace;</a></h2>
<p>创建 <code>.env</code> 文件管理环境变量：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># .env</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_password</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">REDIS_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_redis_password</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_AUTH_TOKEN</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_token_at_least_32_chars</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_USER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio_admin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio_secure_password</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">RABBITMQ_USER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">rabbit_admin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">RABBITMQ_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">rabbit_secure_password</span></span></code></pre>
</div><p>在 <code>docker-compose.yml</code> 中引用：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}</span></span></code></pre>
</div><h2 id="数据持久化" tabindex="-1">数据持久化 <a class="header-anchor" href="#数据持久化" aria-label="Permalink to &quot;数据持久化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="使用命名卷-推荐" tabindex="-1">使用命名卷（推荐） <a class="header-anchor" href="#使用命名卷-推荐" aria-label="Permalink to &quot;使用命名卷（推荐）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql-data:/var/lib/mysql</span></span></code></pre>
</div><div class="warning custom-block"><p class="custom-block-title">仓库自带 compose 目前未挂载 Volume</p>
<p><code>wemirr-platform/附件/docker/docker-compose.yml</code> 以“开箱即用”为主，默认未挂载数据卷。</p>
<p>如果你用于长期开发环境，建议你在本地 copy 一份 compose 并增加 volume 挂载，避免容器删除导致数据丢失。</p>
</div>
<h3 id="使用本地目录" tabindex="-1">使用本地目录 <a class="header-anchor" href="#使用本地目录" aria-label="Permalink to &quot;使用本地目录&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./data/mysql:/var/lib/mysql</span></span></code></pre>
</div><h2 id="故障排查" tabindex="-1">故障排查 <a class="header-anchor" href="#故障排查" aria-label="Permalink to &quot;故障排查&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="服务启动失败" tabindex="-1">服务启动失败 <a class="header-anchor" href="#服务启动失败" aria-label="Permalink to &quot;服务启动失败&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看失败原因</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> service-name</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查容器状态</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -a</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 进入容器调试</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> service-name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sh</span></span></code></pre>
</div><h3 id="经典错误-network-wemirr-not-found" tabindex="-1">经典错误：<code>network wemirr not found</code> <a class="header-anchor" href="#经典错误-network-wemirr-not-found" aria-label="Permalink to &quot;经典错误：`network wemirr not found`&quot;">&ZeroWidthSpace;</a></h3>
<p>原因：未创建外部网络 <code>wemirr</code>。</p>
<p>解决：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span></code></pre>
</div><h3 id="nacos-启动失败" tabindex="-1">Nacos 启动失败 <a class="header-anchor" href="#nacos-启动失败" aria-label="Permalink to &quot;Nacos 启动失败&quot;">&ZeroWidthSpace;</a></h3>
<p>常见原因：MySQL 未就绪</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 先单独启动 MySQL</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 等待 MySQL 就绪后再启动 Nacos</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><h3 id="端口冲突" tabindex="-1">端口冲突 <a class="header-anchor" href="#端口冲突" aria-label="Permalink to &quot;端口冲突&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:3306</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:8848</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 修改 docker-compose.yml 中的端口映射</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "13306:3306"</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 改为 13306</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/docker/production.html">生产环境部署</a> - 学习生产级别的部署配置</li>
<li><a href="/zh/guide/env/deploy.html">环境部署</a> - 部署后端和前端服务</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Docker 入门 ]]></title>
            <link>https://docs.battcn.com/zh/guide/docker/</link>
            <guid>https://docs.battcn.com/zh/guide/docker/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="docker-入门" tabindex="-1">Docker 入门 <a class="header-anchor" href="#docker-入门" aria-label="Permalink to &quot;Docker 入门&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Docker 基础知识，为容器化部署 Wemirr Platform 打下基础</p>
</div>
<h2 id="什么是-docker" tabindex="-1">什么是 Docker？ <a class="header-anchor" href="#什么是-docker" aria-label="Permalink to &quot;什么是 Docker？&quot;">&ZeroWidthSpace;</a></h2>
<p>Docker 是一个开源的容器化平台，可以将应用及其依赖打包成一个轻量级的容器，实现&quot;一次构建，到处运行&quot;。</p>
<h3 id="docker-vs-传统部署" tabindex="-1">Docker vs 传统部署 <a class="header-anchor" href="#docker-vs-传统部署" aria-label="Permalink to &quot;Docker vs 传统部署&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>对比项</th>
<th>传统部署</th>
<th>Docker 部署</th>
</tr>
</thead>
<tbody>
<tr>
<td>环境一致性</td>
<td>环境差异大</td>
<td>环境完全一致</td>
</tr>
<tr>
<td>部署速度</td>
<td>手动安装依赖，耗时长</td>
<td>秒级启动</td>
</tr>
<tr>
<td>资源隔离</td>
<td>进程级别</td>
<td>容器级别</td>
</tr>
<tr>
<td>迁移难度</td>
<td>复杂</td>
<td>简单</td>
</tr>
<tr>
<td>扩展性</td>
<td>手动扩展</td>
<td>一键扩展</td>
</tr>
</tbody>
</table>
<h2 id="安装-docker" tabindex="-1">安装 Docker <a class="header-anchor" href="#安装-docker" aria-label="Permalink to &quot;安装 Docker&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="macos" tabindex="-1">macOS <a class="header-anchor" href="#macos" aria-label="Permalink to &quot;macOS&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 方式一：下载 Docker Desktop</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># https://www.docker.com/products/docker-desktop/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 方式二：使用 Homebrew</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">brew</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --cask</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span></code></pre>
</div><h3 id="windows" tabindex="-1">Windows <a class="header-anchor" href="#windows" aria-label="Permalink to &quot;Windows&quot;">&ZeroWidthSpace;</a></h3>
<ol>
<li>下载 <a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noreferrer">Docker Desktop for Windows</a></li>
<li>启用 WSL 2（推荐）或 Hyper-V</li>
<li>安装并启动 Docker Desktop</li>
</ol>
<h3 id="linux-ubuntu-debian" tabindex="-1">Linux (Ubuntu/Debian) <a class="header-anchor" href="#linux-ubuntu-debian" aria-label="Permalink to &quot;Linux (Ubuntu/Debian)&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 更新包索引</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装依赖</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ca-certificates</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> curl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gnupg</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> lsb-release</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 添加 Docker 官方 GPG 密钥</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/apt/keyrings</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -fsSL</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://download.docker.com/linux/ubuntu/gpg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gpg</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --dearmor</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -o</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/apt/keyrings/docker.gpg</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 设置仓库</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">echo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "deb [arch=$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dpkg</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --print-architecture</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsb_release</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -cs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">) stable"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> tee</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/apt/sources.list.d/docker.list</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/null</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装 Docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker-ce</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker-ce-cli</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> containerd.io</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker-compose-plugin</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 Docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> systemctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> systemctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 添加当前用户到 docker 组（免 sudo）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> usermod</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -aG</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $USER</span></span></code></pre>
</div><h3 id="验证安装" tabindex="-1">验证安装 <a class="header-anchor" href="#验证安装" aria-label="Permalink to &quot;验证安装&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看版本</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --version</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --version</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 运行测试容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> hello-world</span></span></code></pre>
</div><h2 id="docker-核心概念" tabindex="-1">Docker 核心概念 <a class="header-anchor" href="#docker-核心概念" aria-label="Permalink to &quot;Docker 核心概念&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="镜像-image" tabindex="-1">镜像（Image） <a class="header-anchor" href="#镜像-image" aria-label="Permalink to &quot;镜像（Image）&quot;">&ZeroWidthSpace;</a></h3>
<p>镜像是一个只读模板，包含创建容器的指令。类比：安装包/ISO 文件。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 搜索镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> search</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 拉取镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看本地镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> images</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rmi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span></code></pre>
</div><h3 id="容器-container" tabindex="-1">容器（Container） <a class="header-anchor" href="#容器-container" aria-label="Permalink to &quot;容器（Container）&quot;">&ZeroWidthSpace;</a></h3>
<p>容器是镜像的运行实例。类比：正在运行的程序。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建并运行容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看运行中的容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看所有容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -a</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> stop</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 进入容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -it</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bash</span></span></code></pre>
</div><h3 id="网络-network" tabindex="-1">网络（Network） <a class="header-anchor" href="#网络-network" aria-label="Permalink to &quot;网络（Network）&quot;">&ZeroWidthSpace;</a></h3>
<p>Docker 网络让容器之间可以相互通信。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ls</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 容器加入网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><div class="tip custom-block"><p class="custom-block-title">为什么要创建 <code>wemirr</code> 网络？</p>
<p>Wemirr 的 <code>附件/docker/docker-compose.yml</code> 默认使用 <strong>外部网络</strong> <code>wemirr</code>（<code>external.name: wemirr</code>）。</p>
<ul>
<li>如果你的机器上还没有创建该网络，直接执行 <code>docker-compose up -d</code> 会失败</li>
<li>生产/测试环境建议统一使用该网络，便于容器互通、迁移</li>
</ul>
</div>
<h3 id="数据卷-volume" tabindex="-1">数据卷（Volume） <a class="header-anchor" href="#数据卷-volume" aria-label="Permalink to &quot;数据卷（Volume）&quot;">&ZeroWidthSpace;</a></h3>
<p>数据卷用于持久化容器数据。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> volume</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 挂载数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data:/var/lib/mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> volume</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ls</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> volume</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data</span></span></code></pre>
</div><h2 id="常用命令速查" tabindex="-1">常用命令速查 <a class="header-anchor" href="#常用命令速查" aria-label="Permalink to &quot;常用命令速查&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="镜像相关" tabindex="-1">镜像相关 <a class="header-anchor" href="#镜像相关" aria-label="Permalink to &quot;镜像相关&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>docker pull &lt;image&gt;</code></td>
<td>拉取镜像</td>
</tr>
<tr>
<td><code>docker images</code></td>
<td>列出本地镜像</td>
</tr>
<tr>
<td><code>docker rmi &lt;image&gt;</code></td>
<td>删除镜像</td>
</tr>
<tr>
<td><code>docker build -t &lt;name&gt; .</code></td>
<td>构建镜像</td>
</tr>
<tr>
<td><code>docker tag &lt;image&gt; &lt;new-name&gt;</code></td>
<td>重命名镜像</td>
</tr>
</tbody>
</table>
<h3 id="容器相关" tabindex="-1">容器相关 <a class="header-anchor" href="#容器相关" aria-label="Permalink to &quot;容器相关&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>docker run &lt;image&gt;</code></td>
<td>创建并启动容器</td>
</tr>
<tr>
<td><code>docker ps</code></td>
<td>列出运行中容器</td>
</tr>
<tr>
<td><code>docker ps -a</code></td>
<td>列出所有容器</td>
</tr>
<tr>
<td><code>docker stop &lt;container&gt;</code></td>
<td>停止容器</td>
</tr>
<tr>
<td><code>docker start &lt;container&gt;</code></td>
<td>启动容器</td>
</tr>
<tr>
<td><code>docker restart &lt;container&gt;</code></td>
<td>重启容器</td>
</tr>
<tr>
<td><code>docker rm &lt;container&gt;</code></td>
<td>删除容器</td>
</tr>
<tr>
<td><code>docker logs &lt;container&gt;</code></td>
<td>查看容器日志</td>
</tr>
<tr>
<td><code>docker exec -it &lt;container&gt; bash</code></td>
<td>进入容器</td>
</tr>
</tbody>
</table>
<h3 id="docker-run-常用参数" tabindex="-1">docker run 常用参数 <a class="header-anchor" href="#docker-run-常用参数" aria-label="Permalink to &quot;docker run 常用参数&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>参数</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-d</code></td>
<td>后台运行</td>
<td><code>docker run -d nginx</code></td>
</tr>
<tr>
<td><code>--name</code></td>
<td>指定容器名</td>
<td><code>--name my-nginx</code></td>
</tr>
<tr>
<td><code>-p</code></td>
<td>端口映射</td>
<td><code>-p 8080:80</code></td>
</tr>
<tr>
<td><code>-v</code></td>
<td>数据卷挂载</td>
<td><code>-v /data:/app/data</code></td>
</tr>
<tr>
<td><code>-e</code></td>
<td>环境变量</td>
<td><code>-e MYSQL_ROOT_PASSWORD=123456</code></td>
</tr>
<tr>
<td><code>--net</code></td>
<td>指定网络</td>
<td><code>--net wemirr</code></td>
</tr>
<tr>
<td><code>--restart</code></td>
<td>重启策略</td>
<td><code>--restart always</code></td>
</tr>
</tbody>
</table>
<h2 id="wemirr-中间件安装" tabindex="-1">Wemirr 中间件安装 <a class="header-anchor" href="#wemirr-中间件安装" aria-label="Permalink to &quot;Wemirr 中间件安装&quot;">&ZeroWidthSpace;</a></h2>
<h2 id="wemirr-官方中间件版本" tabindex="-1">Wemirr 官方中间件版本 <a class="header-anchor" href="#wemirr-官方中间件版本" aria-label="Permalink to &quot;Wemirr 官方中间件版本&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr 后端仓库内置了可直接使用的中间件编排文件：</p>
<ul>
<li>路径：<code>wemirr-platform/附件/docker/docker-compose.yml</code></li>
</ul>
<p>该文件内的镜像版本如下（<strong>建议以此为准，避免文档与实际不一致</strong>）：</p>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>镜像</th>
<th>版本</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td><code>mysql</code></td>
<td><code>8.0.19</code></td>
</tr>
<tr>
<td>Redis</td>
<td><code>redis</code></td>
<td><code>5.0</code></td>
</tr>
<tr>
<td>Nacos</td>
<td><code>nacos/nacos-server</code></td>
<td><code>2.4.2</code></td>
</tr>
<tr>
<td>RabbitMQ</td>
<td><code>docker.io/macintoshplus/rabbitmq-management</code></td>
<td>镜像未固定 tag（以仓库文件为准）</td>
</tr>
</tbody>
</table>
<h3 id="创建网络" tabindex="-1">创建网络 <a class="header-anchor" href="#创建网络" aria-label="Permalink to &quot;创建网络&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><h3 id="mysql" tabindex="-1">MySQL <a class="header-anchor" href="#mysql" aria-label="Permalink to &quot;MySQL&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data:/var/lib/mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TZ=Asia/Shanghai</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  mysql:8.0.19</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --character-set-server=utf8mb4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --collation-server=utf8mb4_general_ci</span></span></code></pre>
</div><h3 id="redis" tabindex="-1">Redis <a class="header-anchor" href="#redis" aria-label="Permalink to &quot;Redis&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis-data:/data</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  redis:5.0</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  redis-server</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --appendonly</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> yes</span></span></code></pre>
</div><h3 id="nacos" tabindex="-1">Nacos <a class="header-anchor" href="#nacos" aria-label="Permalink to &quot;Nacos&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9849:9849</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  nacos/nacos-server:2.4.2</span></span></code></pre>
</div><div class="warning custom-block"><p class="custom-block-title">关于 Nacos 数据源</p>
<p>仓库内置的 <code>附件/docker/docker-compose.yml</code> 采用 <strong>standalone</strong> 模式启动 Nacos，并未强制绑定 MySQL。</p>
<p>如果你希望让 Nacos 元数据落库到 MySQL，请以 Nacos 官方文档为准增加数据库相关环境变量，并确保初始化 Nacos 的表结构。</p>
</div>
<h3 id="rabbitmq-动态数据源-消息总线相关" tabindex="-1">RabbitMQ（动态数据源/消息总线相关） <a class="header-anchor" href="#rabbitmq-动态数据源-消息总线相关" aria-label="Permalink to &quot;RabbitMQ（动态数据源/消息总线相关）&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr 仓库提供的编排文件里包含 RabbitMQ（管理端口也已映射）：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-rabbitmq</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  docker.io/macintoshplus/rabbitmq-management</span></span></code></pre>
</div><p>管理后台：</p>
<ul>
<li><code>http://localhost:15672</code></li>
</ul>
<blockquote>
<p>账号密码以该镜像默认配置为准；如果你在生产环境使用，建议自定义账号/密码并限制端口暴露。</p>
</blockquote>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/docker/compose.html">Docker Compose 一键部署</a> - 使用 Compose 快速部署整套环境</li>
<li><a href="/zh/guide/docker/production.html">生产环境部署</a> - 学习生产级别的部署方案</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="docker-入门" tabindex="-1">Docker 入门 <a class="header-anchor" href="#docker-入门" aria-label="Permalink to &quot;Docker 入门&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Docker 基础知识，为容器化部署 Wemirr Platform 打下基础</p>
</div>
<h2 id="什么是-docker" tabindex="-1">什么是 Docker？ <a class="header-anchor" href="#什么是-docker" aria-label="Permalink to &quot;什么是 Docker？&quot;">&ZeroWidthSpace;</a></h2>
<p>Docker 是一个开源的容器化平台，可以将应用及其依赖打包成一个轻量级的容器，实现&quot;一次构建，到处运行&quot;。</p>
<h3 id="docker-vs-传统部署" tabindex="-1">Docker vs 传统部署 <a class="header-anchor" href="#docker-vs-传统部署" aria-label="Permalink to &quot;Docker vs 传统部署&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>对比项</th>
<th>传统部署</th>
<th>Docker 部署</th>
</tr>
</thead>
<tbody>
<tr>
<td>环境一致性</td>
<td>环境差异大</td>
<td>环境完全一致</td>
</tr>
<tr>
<td>部署速度</td>
<td>手动安装依赖，耗时长</td>
<td>秒级启动</td>
</tr>
<tr>
<td>资源隔离</td>
<td>进程级别</td>
<td>容器级别</td>
</tr>
<tr>
<td>迁移难度</td>
<td>复杂</td>
<td>简单</td>
</tr>
<tr>
<td>扩展性</td>
<td>手动扩展</td>
<td>一键扩展</td>
</tr>
</tbody>
</table>
<h2 id="安装-docker" tabindex="-1">安装 Docker <a class="header-anchor" href="#安装-docker" aria-label="Permalink to &quot;安装 Docker&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="macos" tabindex="-1">macOS <a class="header-anchor" href="#macos" aria-label="Permalink to &quot;macOS&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 方式一：下载 Docker Desktop</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># https://www.docker.com/products/docker-desktop/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 方式二：使用 Homebrew</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">brew</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --cask</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span></code></pre>
</div><h3 id="windows" tabindex="-1">Windows <a class="header-anchor" href="#windows" aria-label="Permalink to &quot;Windows&quot;">&ZeroWidthSpace;</a></h3>
<ol>
<li>下载 <a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noreferrer">Docker Desktop for Windows</a></li>
<li>启用 WSL 2（推荐）或 Hyper-V</li>
<li>安装并启动 Docker Desktop</li>
</ol>
<h3 id="linux-ubuntu-debian" tabindex="-1">Linux (Ubuntu/Debian) <a class="header-anchor" href="#linux-ubuntu-debian" aria-label="Permalink to &quot;Linux (Ubuntu/Debian)&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 更新包索引</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装依赖</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ca-certificates</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> curl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gnupg</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> lsb-release</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 添加 Docker 官方 GPG 密钥</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/apt/keyrings</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -fsSL</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://download.docker.com/linux/ubuntu/gpg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gpg</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --dearmor</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -o</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/apt/keyrings/docker.gpg</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 设置仓库</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">echo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "deb [arch=$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dpkg</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --print-architecture</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsb_release</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -cs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">) stable"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> tee</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/apt/sources.list.d/docker.list</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/null</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装 Docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker-ce</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker-ce-cli</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> containerd.io</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker-compose-plugin</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 Docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> systemctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> systemctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 添加当前用户到 docker 组（免 sudo）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> usermod</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -aG</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $USER</span></span></code></pre>
</div><h3 id="验证安装" tabindex="-1">验证安装 <a class="header-anchor" href="#验证安装" aria-label="Permalink to &quot;验证安装&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看版本</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --version</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --version</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 运行测试容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> hello-world</span></span></code></pre>
</div><h2 id="docker-核心概念" tabindex="-1">Docker 核心概念 <a class="header-anchor" href="#docker-核心概念" aria-label="Permalink to &quot;Docker 核心概念&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="镜像-image" tabindex="-1">镜像（Image） <a class="header-anchor" href="#镜像-image" aria-label="Permalink to &quot;镜像（Image）&quot;">&ZeroWidthSpace;</a></h3>
<p>镜像是一个只读模板，包含创建容器的指令。类比：安装包/ISO 文件。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 搜索镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> search</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 拉取镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看本地镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> images</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rmi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span></code></pre>
</div><h3 id="容器-container" tabindex="-1">容器（Container） <a class="header-anchor" href="#容器-container" aria-label="Permalink to &quot;容器（Container）&quot;">&ZeroWidthSpace;</a></h3>
<p>容器是镜像的运行实例。类比：正在运行的程序。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建并运行容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看运行中的容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看所有容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -a</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 停止容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> stop</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 进入容器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -it</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> my-mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bash</span></span></code></pre>
</div><h3 id="网络-network" tabindex="-1">网络（Network） <a class="header-anchor" href="#网络-network" aria-label="Permalink to &quot;网络（Network）&quot;">&ZeroWidthSpace;</a></h3>
<p>Docker 网络让容器之间可以相互通信。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ls</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 容器加入网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><div class="tip custom-block"><p class="custom-block-title">为什么要创建 <code>wemirr</code> 网络？</p>
<p>Wemirr 的 <code>附件/docker/docker-compose.yml</code> 默认使用 <strong>外部网络</strong> <code>wemirr</code>（<code>external.name: wemirr</code>）。</p>
<ul>
<li>如果你的机器上还没有创建该网络，直接执行 <code>docker-compose up -d</code> 会失败</li>
<li>生产/测试环境建议统一使用该网络，便于容器互通、迁移</li>
</ul>
</div>
<h3 id="数据卷-volume" tabindex="-1">数据卷（Volume） <a class="header-anchor" href="#数据卷-volume" aria-label="Permalink to &quot;数据卷（Volume）&quot;">&ZeroWidthSpace;</a></h3>
<p>数据卷用于持久化容器数据。</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> volume</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 挂载数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data:/var/lib/mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> volume</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ls</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 删除数据卷</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> volume</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data</span></span></code></pre>
</div><h2 id="常用命令速查" tabindex="-1">常用命令速查 <a class="header-anchor" href="#常用命令速查" aria-label="Permalink to &quot;常用命令速查&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="镜像相关" tabindex="-1">镜像相关 <a class="header-anchor" href="#镜像相关" aria-label="Permalink to &quot;镜像相关&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>docker pull &lt;image&gt;</code></td>
<td>拉取镜像</td>
</tr>
<tr>
<td><code>docker images</code></td>
<td>列出本地镜像</td>
</tr>
<tr>
<td><code>docker rmi &lt;image&gt;</code></td>
<td>删除镜像</td>
</tr>
<tr>
<td><code>docker build -t &lt;name&gt; .</code></td>
<td>构建镜像</td>
</tr>
<tr>
<td><code>docker tag &lt;image&gt; &lt;new-name&gt;</code></td>
<td>重命名镜像</td>
</tr>
</tbody>
</table>
<h3 id="容器相关" tabindex="-1">容器相关 <a class="header-anchor" href="#容器相关" aria-label="Permalink to &quot;容器相关&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>docker run &lt;image&gt;</code></td>
<td>创建并启动容器</td>
</tr>
<tr>
<td><code>docker ps</code></td>
<td>列出运行中容器</td>
</tr>
<tr>
<td><code>docker ps -a</code></td>
<td>列出所有容器</td>
</tr>
<tr>
<td><code>docker stop &lt;container&gt;</code></td>
<td>停止容器</td>
</tr>
<tr>
<td><code>docker start &lt;container&gt;</code></td>
<td>启动容器</td>
</tr>
<tr>
<td><code>docker restart &lt;container&gt;</code></td>
<td>重启容器</td>
</tr>
<tr>
<td><code>docker rm &lt;container&gt;</code></td>
<td>删除容器</td>
</tr>
<tr>
<td><code>docker logs &lt;container&gt;</code></td>
<td>查看容器日志</td>
</tr>
<tr>
<td><code>docker exec -it &lt;container&gt; bash</code></td>
<td>进入容器</td>
</tr>
</tbody>
</table>
<h3 id="docker-run-常用参数" tabindex="-1">docker run 常用参数 <a class="header-anchor" href="#docker-run-常用参数" aria-label="Permalink to &quot;docker run 常用参数&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>参数</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-d</code></td>
<td>后台运行</td>
<td><code>docker run -d nginx</code></td>
</tr>
<tr>
<td><code>--name</code></td>
<td>指定容器名</td>
<td><code>--name my-nginx</code></td>
</tr>
<tr>
<td><code>-p</code></td>
<td>端口映射</td>
<td><code>-p 8080:80</code></td>
</tr>
<tr>
<td><code>-v</code></td>
<td>数据卷挂载</td>
<td><code>-v /data:/app/data</code></td>
</tr>
<tr>
<td><code>-e</code></td>
<td>环境变量</td>
<td><code>-e MYSQL_ROOT_PASSWORD=123456</code></td>
</tr>
<tr>
<td><code>--net</code></td>
<td>指定网络</td>
<td><code>--net wemirr</code></td>
</tr>
<tr>
<td><code>--restart</code></td>
<td>重启策略</td>
<td><code>--restart always</code></td>
</tr>
</tbody>
</table>
<h2 id="wemirr-中间件安装" tabindex="-1">Wemirr 中间件安装 <a class="header-anchor" href="#wemirr-中间件安装" aria-label="Permalink to &quot;Wemirr 中间件安装&quot;">&ZeroWidthSpace;</a></h2>
<h2 id="wemirr-官方中间件版本" tabindex="-1">Wemirr 官方中间件版本 <a class="header-anchor" href="#wemirr-官方中间件版本" aria-label="Permalink to &quot;Wemirr 官方中间件版本&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr 后端仓库内置了可直接使用的中间件编排文件：</p>
<ul>
<li>路径：<code>wemirr-platform/附件/docker/docker-compose.yml</code></li>
</ul>
<p>该文件内的镜像版本如下（<strong>建议以此为准，避免文档与实际不一致</strong>）：</p>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>镜像</th>
<th>版本</th>
</tr>
</thead>
<tbody>
<tr>
<td>MySQL</td>
<td><code>mysql</code></td>
<td><code>8.0.19</code></td>
</tr>
<tr>
<td>Redis</td>
<td><code>redis</code></td>
<td><code>5.0</code></td>
</tr>
<tr>
<td>Nacos</td>
<td><code>nacos/nacos-server</code></td>
<td><code>2.4.2</code></td>
</tr>
<tr>
<td>RabbitMQ</td>
<td><code>docker.io/macintoshplus/rabbitmq-management</code></td>
<td>镜像未固定 tag（以仓库文件为准）</td>
</tr>
</tbody>
</table>
<h3 id="创建网络" tabindex="-1">创建网络 <a class="header-anchor" href="#创建网络" aria-label="Permalink to &quot;创建网络&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><h3 id="mysql" tabindex="-1">MySQL <a class="header-anchor" href="#mysql" aria-label="Permalink to &quot;MySQL&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql-data:/var/lib/mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TZ=Asia/Shanghai</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  mysql:8.0.19</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --character-set-server=utf8mb4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --collation-server=utf8mb4_general_ci</span></span></code></pre>
</div><h3 id="redis" tabindex="-1">Redis <a class="header-anchor" href="#redis" aria-label="Permalink to &quot;Redis&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -v</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis-data:/data</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  redis:5.0</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  redis-server</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --appendonly</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> yes</span></span></code></pre>
</div><h3 id="nacos" tabindex="-1">Nacos <a class="header-anchor" href="#nacos" aria-label="Permalink to &quot;Nacos&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9849:9849</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  nacos/nacos-server:2.4.2</span></span></code></pre>
</div><div class="warning custom-block"><p class="custom-block-title">关于 Nacos 数据源</p>
<p>仓库内置的 <code>附件/docker/docker-compose.yml</code> 采用 <strong>standalone</strong> 模式启动 Nacos，并未强制绑定 MySQL。</p>
<p>如果你希望让 Nacos 元数据落库到 MySQL，请以 Nacos 官方文档为准增加数据库相关环境变量，并确保初始化 Nacos 的表结构。</p>
</div>
<h3 id="rabbitmq-动态数据源-消息总线相关" tabindex="-1">RabbitMQ（动态数据源/消息总线相关） <a class="header-anchor" href="#rabbitmq-动态数据源-消息总线相关" aria-label="Permalink to &quot;RabbitMQ（动态数据源/消息总线相关）&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr 仓库提供的编排文件里包含 RabbitMQ（管理端口也已映射）：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-rabbitmq</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  docker.io/macintoshplus/rabbitmq-management</span></span></code></pre>
</div><p>管理后台：</p>
<ul>
<li><code>http://localhost:15672</code></li>
</ul>
<blockquote>
<p>账号密码以该镜像默认配置为准；如果你在生产环境使用，建议自定义账号/密码并限制端口暴露。</p>
</blockquote>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/docker/compose.html">Docker Compose 一键部署</a> - 使用 Compose 快速部署整套环境</li>
<li><a href="/zh/guide/docker/production.html">生产环境部署</a> - 学习生产级别的部署方案</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[生产环境部署 ]]></title>
            <link>https://docs.battcn.com/zh/guide/docker/production.html</link>
            <guid>https://docs.battcn.com/zh/guide/docker/production.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="生产环境部署" tabindex="-1">生产环境部署 <a class="header-anchor" href="#生产环境部署" aria-label="Permalink to &quot;生产环境部署&quot;">&ZeroWidthSpace;</a></h1>
<div class="warning custom-block"><p class="custom-block-title">重要提醒</p>
<p>生产环境部署需要格外注意安全性、可用性和性能，请仔细阅读本文档</p>
</div>
<h2 id="部署架构" tabindex="-1">部署架构 <a class="header-anchor" href="#部署架构" aria-label="Permalink to &quot;部署架构&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="单机部署架构" tabindex="-1">单机部署架构 <a class="header-anchor" href="#单机部署架构" aria-label="Permalink to &quot;单机部署架构&quot;">&ZeroWidthSpace;</a></h3>
<p>适合小型项目、测试环境：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>                    ┌─────────────────┐</span></span>
<span class="line"><span>                    │     Nginx       │</span></span>
<span class="line"><span>                    │   (443/80)      │</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>        │  前端静态  │  │  Gateway  │  │   Nacos   │</span></span>
<span class="line"><span>        │   文件    │  │   9000    │  │   8848    │</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>              │   IAM    │ │Suite │ │Plugin│</span></span>
<span class="line"><span>              │   5001   │ │ 5002 │ │ ...  │</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>              │  MySQL  │ │ Redis │ │ MinIO │</span></span>
<span class="line"><span>              │  3306   │ │ 6379  │ │ 9000  │</span></span>
<span class="line"><span>              └─────────┘ └───────┘ └───────┘</span></span></code></pre>
</div><h3 id="高可用集群架构" tabindex="-1">高可用集群架构 <a class="header-anchor" href="#高可用集群架构" aria-label="Permalink to &quot;高可用集群架构&quot;">&ZeroWidthSpace;</a></h3>
<p>适合中大型项目、生产环境：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>                         ┌──────────────┐</span></span>
<span class="line"><span>                         │   SLB/LB     │</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>        │  Nginx-1  │     │  Nginx-2  │     │  Nginx-3  │</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>        │ Gateway-1 │     │ Gateway-2 │     │ Gateway-3 │</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>                          │   Nacos   │</span></span>
<span class="line"><span>                          │  Cluster  │</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>  │ IAM × N   │           │ Suite × N │           │ Plugin × N│</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>        │MySQL主从  │     │Redis集群  │     │MinIO集群  │</span></span>
<span class="line"><span>        └───────────┘     └───────────┘     └───────────┘</span></span></code></pre>
</div><h2 id="生产级-docker-compose" tabindex="-1">生产级 Docker Compose <a class="header-anchor" href="#生产级-docker-compose" aria-label="Permalink to &quot;生产级 Docker Compose&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="目录结构" tabindex="-1">目录结构 <a class="header-anchor" href="#目录结构" aria-label="Permalink to &quot;目录结构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>/opt/wemirr/</span></span>
<span class="line"><span>├── docker-compose.yml</span></span>
<span class="line"><span>├── .env</span></span>
<span class="line"><span>├── nginx/</span></span>
<span class="line"><span>│   ├── nginx.conf</span></span>
<span class="line"><span>│   ├── conf.d/</span></span>
<span class="line"><span>│   │   └── default.conf</span></span>
<span class="line"><span>│   └── ssl/</span></span>
<span class="line"><span>│       ├── cert.pem</span></span>
<span class="line"><span>│       └── key.pem</span></span>
<span class="line"><span>├── mysql/</span></span>
<span class="line"><span>│   ├── conf/</span></span>
<span class="line"><span>│   │   └── my.cnf</span></span>
<span class="line"><span>│   └── init/</span></span>
<span class="line"><span>│       └── init.sql</span></span>
<span class="line"><span>├── redis/</span></span>
<span class="line"><span>│   └── redis.conf</span></span>
<span class="line"><span>├── logs/</span></span>
<span class="line"><span>└── data/</span></span></code></pre>
</div><h3 id="docker-compose-yml" tabindex="-1">docker-compose.yml <a class="header-anchor" href="#docker-compose-yml" aria-label="Permalink to &quot;docker-compose.yml&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'3.8'</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  wemirr-net</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    driver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">bridge</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nacos-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  minio-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  gateway-logs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  iam-logs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  suite-logs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # ==================== 中间件层 ====================</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql:8.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-mysql</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"3306:3306"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql-data:/var/lib/mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./mysql/init:/docker-entrypoint-initdb.d</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    command</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--character-set-server=utf8mb4</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--collation-server=utf8mb4_general_ci</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--lower_case_table_names=1</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--max_connections=1000</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--innodb_buffer_pool_size=1G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">2G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    healthcheck</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CMD"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"mysqladmin"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ping"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-h"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"localhost"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-p${MYSQL_ROOT_PASSWORD}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      interval</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">10s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">5s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis:7-alpine</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"6379:6379"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis-data:/data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./redis/redis.conf:/usr/local/etc/redis/redis.conf</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    command</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis-server /usr/local/etc/redis/redis.conf</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">512M</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    healthcheck</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CMD"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"redis-cli"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-a"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"${REDIS_PASSWORD}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ping"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      interval</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">10s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">5s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos/nacos-server:v2.3.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-nacos</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8848:8848"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9848:9848"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9849:9849"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MODE=standalone</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_DATASOURCE_PLATFORM=mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_HOST=mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_PORT=3306</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_DB_NAME=nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_USER=root</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_PASSWORD=${MYSQL_ROOT_PASSWORD}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_ENABLE=true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_TOKEN=${NACOS_AUTH_TOKEN}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_IDENTITY_KEY=serverIdentity</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_IDENTITY_VALUE=${NACOS_IDENTITY_VALUE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JVM_XMS=512m</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JVM_XMX=1g</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos-data:/home/nacos/data</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        condition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">service_healthy</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  minio</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">bitnami/minio:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-minio</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"19000:9000"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"19001:9001"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MINIO_ROOT_USER=${MINIO_ROOT_USER}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MINIO_DEFAULT_BUCKETS=wemirr-bucket</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio-data:/bitnami/minio/data</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">512M</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # ==================== 应用层 ====================</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-gateway:${APP_VERSION}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9000:9000"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_PROFILES_ACTIVE=prod</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_SERVER_ADDR=nacos:8848</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_NAMESPACE=${NACOS_NAMESPACE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">gateway-logs:/app/logs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    healthcheck</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CMD"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"curl"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-f"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"http://localhost:9000/actuator/health"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      interval</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">30s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">10s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  iam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-iam:${APP_VERSION}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"5001:5001"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_PROFILES_ACTIVE=prod</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_SERVER_ADDR=nacos:8848</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_NAMESPACE=${NACOS_NAMESPACE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">iam-logs:/app/logs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  suite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-suite:${APP_VERSION}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-suite</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"5002:5002"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_PROFILES_ACTIVE=prod</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_SERVER_ADDR=nacos:8848</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_NAMESPACE=${NACOS_NAMESPACE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">suite-logs:/app/logs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # ==================== 反向代理 ====================</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nginx</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nginx:alpine</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-nginx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"80:80"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"443:443"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/nginx.conf:/etc/nginx/nginx.conf</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/conf.d:/etc/nginx/conf.d</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/ssl:/etc/nginx/ssl</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/html:/usr/share/nginx/html</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./logs/nginx:/var/log/nginx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span></code></pre>
</div><h3 id="env-文件" tabindex="-1">.env 文件 <a class="header-anchor" href="#env-文件" aria-label="Permalink to &quot;.env 文件&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># MySQL</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_mysql_password_here</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Redis</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">REDIS_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_redis_password_here</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_AUTH_TOKEN</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SecretKey012345678901234567890123456789012345678901234567890123456789</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_IDENTITY_VALUE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_identity_value</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_NAMESPACE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">prod</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># MinIO</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_USER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio_admin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_minio_password_here</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 应用版本</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">APP_VERSION</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">v4.0.0</span></span></code></pre>
</div><h3 id="nginx-配置" tabindex="-1">Nginx 配置 <a class="header-anchor" href="#nginx-配置" aria-label="Permalink to &quot;Nginx 配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># nginx/conf.d/default.conf</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">upstream</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> gateway </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> gateway:9000;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    keepalive </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your-domain.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 301</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> https://$server_name$request_uri;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl http2;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your-domain.com;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/ssl/cert.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/ssl/key.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_session_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1d</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_session_cache </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">shared:SSL:50m;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_protocols </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TLSv1.2 TLSv1.3;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_ciphers </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_prefer_server_ciphers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 安全头</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Frame-Options </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"SAMEORIGIN"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> always;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Content-Type-Options </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"nosniff"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> always;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-XSS-Protection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"1; mode=block"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> always;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 前端静态资源</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/usr/share/nginx/html;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        index </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">index.html;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try_files </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$uri $uri/ /index.html;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 静态资源缓存</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        location</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ~*</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"> \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            expires </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30d</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Cache-Control </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"public, immutable"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # API 代理</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api/ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://gateway/;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_http_version </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Host $host;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Real-IP $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-Proto $scheme;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">""</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 超时设置</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_connect_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60s</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_send_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60s</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_read_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60s</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # WebSocket 支持</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Upgrade $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 健康检查</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /health </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 200</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'OK'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Content-Type text/plain;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="使用仓库自带-nginx-配置-推荐先对齐" tabindex="-1">使用仓库自带 Nginx 配置（推荐先对齐） <a class="header-anchor" href="#使用仓库自带-nginx-配置-推荐先对齐" aria-label="Permalink to &quot;使用仓库自带 Nginx 配置（推荐先对齐）&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr 后端仓库在 <code>附件/nginx/</code> 目录提供了作者线上环境使用的 Nginx 配置，适合你在生产环境落地时参考并做二次调整：</p>
<ul>
<li><code>wemirr-platform/附件/nginx/nginx-http.conf</code></li>
<li><code>wemirr-platform/附件/nginx/nginx-https.conf</code></li>
</ul>
<blockquote>
<p>说明：配置中默认域名为 <code>cloud.battcn.com</code>，静态资源目录为 <code>/opt/wemirr-platform/cloud-ui/dist</code>，你需要替换为自己的域名与前端产物路径。</p>
</blockquote>
<h3 id="http-版本示例-来自仓库" tabindex="-1">HTTP 版本示例（来自仓库） <a class="header-anchor" href="#http-版本示例-来自仓库" aria-label="Permalink to &quot;HTTP 版本示例（来自仓库）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/opt/wemirr-platform/cloud-ui/dist;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        gzip_static </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api/ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:9000/;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_redirect </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Host $host;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Real-IP $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_http_version </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Upgrade $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_max_body_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_body_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_connect_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_send_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_read_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_busy_buffers_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_temp_file_write_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="https-版本示例-来自仓库" tabindex="-1">HTTPS 版本示例（来自仓库） <a class="header-anchor" href="#https-版本示例-来自仓库" aria-label="Permalink to &quot;HTTPS 版本示例（来自仓库）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    rewrite</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"> ^(.*)$</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  https://cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 证书是从阿里云申请的免费证书</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /usr/local/nginx/conf/myconf/cert/cloud/4869096_cloud.battcn.com.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /usr/local/nginx/conf/myconf/cert/cloud/4869096_cloud.battcn.com.key;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_session_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_ciphers </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_protocols </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TLSv1 TLSv1.1 TLSv1.2;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_prefer_server_ciphers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/opt/wemirr-platform/cloud-ui/dist;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        gzip_static </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api/ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:9000/;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_redirect </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Host $host;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Real-IP $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_http_version </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Upgrade $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_max_body_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_body_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_connect_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_send_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_read_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_busy_buffers_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_temp_file_write_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="静态资源路径建议" tabindex="-1">静态资源路径建议 <a class="header-anchor" href="#静态资源路径建议" aria-label="Permalink to &quot;静态资源路径建议&quot;">&ZeroWidthSpace;</a></h3>
<p>配置中的 <code>root /opt/wemirr-platform/cloud-ui/dist;</code> 是作者线上路径。</p>
<p>如果你的前端是通过 <code>wemirr-platform-ui</code> 构建产物部署，建议将产物解压到类似目录：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>/opt/wemirr-platform/</span></span>
<span class="line"><span>└── cloud-ui/</span></span>
<span class="line"><span>    └── dist/</span></span></code></pre>
</div><p>并保持与 Nginx <code>root</code> 一致。</p>
<h2 id="生产部署检查清单" tabindex="-1">生产部署检查清单 <a class="header-anchor" href="#生产部署检查清单" aria-label="Permalink to &quot;生产部署检查清单&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-端口暴露" tabindex="-1">1. 端口暴露 <a class="header-anchor" href="#_1-端口暴露" aria-label="Permalink to &quot;1. 端口暴露&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>对外只暴露：<code>80/443</code></li>
<li><code>3306/6379/8848/15672</code> 等中间件端口：
<ul>
<li>建议只在内网访问</li>
<li>或通过防火墙限制来源 IP</li>
</ul>
</li>
</ul>
<h3 id="_2-数据持久化" tabindex="-1">2. 数据持久化 <a class="header-anchor" href="#_2-数据持久化" aria-label="Permalink to &quot;2. 数据持久化&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>MySQL：必须挂载 <code>/var/lib/mysql</code></li>
<li>Redis：建议开启 AOF 并挂载 <code>/data</code></li>
<li>Nacos：建议挂载 <code>/home/nacos/data</code></li>
</ul>
<h3 id="_3-备份与恢复演练" tabindex="-1">3. 备份与恢复演练 <a class="header-anchor" href="#_3-备份与恢复演练" aria-label="Permalink to &quot;3. 备份与恢复演练&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>至少保证：数据库备份 + Nacos 配置备份</li>
<li>建议每月至少做一次恢复演练</li>
</ul>
<h3 id="_4-配置与密钥" tabindex="-1">4. 配置与密钥 <a class="header-anchor" href="#_4-配置与密钥" aria-label="Permalink to &quot;4. 配置与密钥&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><code>.env</code> 与证书文件不要提交到仓库</li>
<li>Nacos Token、Redis 密码、MySQL 密码必须使用强密码</li>
</ul>
<h3 id="mysql-配置" tabindex="-1">MySQL 配置 <a class="header-anchor" href="#mysql-配置" aria-label="Permalink to &quot;MySQL 配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-ini vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># mysql/conf/my.cnf</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[mysqld]</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">character-set-server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=utf8mb4</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">collation-server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=utf8mb4_general_ci</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">lower_case_table_names</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 连接数</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">max_connections</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1000</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">max_connect_errors</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=100</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># InnoDB</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_buffer_pool_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1G</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_log_file_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=256M</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_flush_log_at_trx_commit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=2</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_flush_method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=O_DIRECT</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 日志</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">slow_query_log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">slow_query_log_file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=/var/lib/mysql/slow.log</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">long_query_time</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=2</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 二进制日志（主从复制需要）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server-id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">log_bin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=mysql-bin</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">binlog_format</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=ROW</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">expire_logs_days</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=7</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[client]</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default-character-set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=utf8mb4</span></span></code></pre>
</div><h3 id="redis-配置" tabindex="-1">Redis 配置 <a class="header-anchor" href="#redis-配置" aria-label="Permalink to &quot;Redis 配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-conf vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">conf</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span># redis/redis.conf</span></span>
<span class="line"><span>bind 0.0.0.0</span></span>
<span class="line"><span>port 6379</span></span>
<span class="line"><span>requirepass your_secure_redis_password_here</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 持久化</span></span>
<span class="line"><span>appendonly yes</span></span>
<span class="line"><span>appendfsync everysec</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 内存管理</span></span>
<span class="line"><span>maxmemory 512mb</span></span>
<span class="line"><span>maxmemory-policy allkeys-lru</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 安全</span></span>
<span class="line"><span>rename-command FLUSHALL ""</span></span>
<span class="line"><span>rename-command FLUSHDB ""</span></span>
<span class="line"><span>rename-command CONFIG ""</span></span></code></pre>
</div><h2 id="部署流程" tabindex="-1">部署流程 <a class="header-anchor" href="#部署流程" aria-label="Permalink to &quot;部署流程&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-准备服务器" tabindex="-1">1. 准备服务器 <a class="header-anchor" href="#_1-准备服务器" aria-label="Permalink to &quot;1. 准备服务器&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 更新系统</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> upgrade</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -y</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装 Docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -fsSL</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://get.docker.com</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sh</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> systemctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装 Docker Compose</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -L</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "https://github.com/docker/compose/releases/latest/download/docker-compose-$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">uname</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)-$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">uname</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -o</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /usr/local/bin/docker-compose</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> chmod</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +x</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /usr/local/bin/docker-compose</span></span></code></pre>
</div><h3 id="_2-部署中间件" tabindex="-1">2. 部署中间件 <a class="header-anchor" href="#_2-部署中间件" aria-label="Permalink to &quot;2. 部署中间件&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动中间件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> minio</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 等待服务就绪</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sleep</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查服务状态</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span></span></code></pre>
</div><h3 id="_3-初始化数据" tabindex="-1">3. 初始化数据 <a class="header-anchor" href="#_3-初始化数据" aria-label="Permalink to &quot;3. 初始化数据&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 导入数据库</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -uroot</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> &#x3C;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ./mysql/init/v4-dev.sql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 导入 Nacos 配置（通过 Web 界面或 API）</span></span></code></pre>
</div><h3 id="_4-部署应用" tabindex="-1">4. 部署应用 <a class="header-anchor" href="#_4-部署应用" aria-label="Permalink to &quot;4. 部署应用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动所有应用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gateway</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> iam</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> suite</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nginx</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gateway</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> iam</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> suite</span></span></code></pre>
</div><h3 id="_5-验证部署" tabindex="-1">5. 验证部署 <a class="header-anchor" href="#_5-验证部署" aria-label="Permalink to &quot;5. 验证部署&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 健康检查</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost/health</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost:9000/actuator/health</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 访问系统</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">echo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "请访问: https://your-domain.com"</span></span></code></pre>
</div><h2 id="运维管理" tabindex="-1">运维管理 <a class="header-anchor" href="#运维管理" aria-label="Permalink to &quot;运维管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="日志管理" tabindex="-1">日志管理 <a class="header-anchor" href="#日志管理" aria-label="Permalink to &quot;日志管理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看实时日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --tail=100</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gateway</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 日志轮转配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">cat</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/docker/daemon.json</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> &#x3C;&#x3C;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> EOF</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">{</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "log-driver": "json-file",</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "log-opts": {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "max-size": "100m",</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "max-file": "3"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  }</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">EOF</span></span></code></pre>
</div><h3 id="监控告警" tabindex="-1">监控告警 <a class="header-anchor" href="#监控告警" aria-label="Permalink to &quot;监控告警&quot;">&ZeroWidthSpace;</a></h3>
<p>推荐使用 Prometheus + Grafana 进行监控：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 添加到 docker-compose.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">prometheus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">prom/prometheus:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9090:9090"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">grafana</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">grafana/grafana:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"3000:3000"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">GF_SECURITY_ADMIN_PASSWORD=admin123</span></span></code></pre>
</div><h3 id="备份策略" tabindex="-1">备份策略 <a class="header-anchor" href="#备份策略" aria-label="Permalink to &quot;备份策略&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">#!/bin/bash</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># backup.sh</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BACKUP_DIR</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/opt/wemirr/backup/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">date</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +%Y%m%d</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 备份 MySQL</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysqldump</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -uroot</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">}</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --all-databases</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/mysql.sql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 备份 Redis</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-redis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis-cli</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ${REDIS_PASSWORD} </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BGSAVE</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> cp</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-redis:/data/dump.rdb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 备份配置文件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">cp</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -r</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/.env</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/nginx</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 清理7天前的备份</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">find</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/backup</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -type</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mtime</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +7</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +</span></span></code></pre>
</div><p>添加到 crontab：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">0</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> *</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/backup.sh</span></span></code></pre>
</div><h2 id="安全加固" tabindex="-1">安全加固 <a class="header-anchor" href="#安全加固" aria-label="Permalink to &quot;安全加固&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-防火墙配置" tabindex="-1">1. 防火墙配置 <a class="header-anchor" href="#_1-防火墙配置" aria-label="Permalink to &quot;1. 防火墙配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 只开放必要端口</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> allow</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 80/tcp</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> allow</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 443/tcp</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> allow</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 22/tcp</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span></span></code></pre>
</div><h3 id="_2-密码强度" tabindex="-1">2. 密码强度 <a class="header-anchor" href="#_2-密码强度" aria-label="Permalink to &quot;2. 密码强度&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>MySQL 密码至少 16 位，包含大小写字母、数字、特殊字符</li>
<li>Redis 密码至少 32 位</li>
<li>Nacos Token 至少 64 位</li>
</ul>
<h3 id="_3-定期更新" tabindex="-1">3. 定期更新 <a class="header-anchor" href="#_3-定期更新" aria-label="Permalink to &quot;3. 定期更新&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 更新镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/packages/devops/monitor.html">监控中心</a> - 配置系统监控</li>
<li><a href="/zh/guide/packages/devops/nginx.html">Nginx 配置</a> - 深入了解 Nginx 配置</li>
<li><a href="/zh/faq/">故障排查</a> - 常见问题解决</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="生产环境部署" tabindex="-1">生产环境部署 <a class="header-anchor" href="#生产环境部署" aria-label="Permalink to &quot;生产环境部署&quot;">&ZeroWidthSpace;</a></h1>
<div class="warning custom-block"><p class="custom-block-title">重要提醒</p>
<p>生产环境部署需要格外注意安全性、可用性和性能，请仔细阅读本文档</p>
</div>
<h2 id="部署架构" tabindex="-1">部署架构 <a class="header-anchor" href="#部署架构" aria-label="Permalink to &quot;部署架构&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="单机部署架构" tabindex="-1">单机部署架构 <a class="header-anchor" href="#单机部署架构" aria-label="Permalink to &quot;单机部署架构&quot;">&ZeroWidthSpace;</a></h3>
<p>适合小型项目、测试环境：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>                    ┌─────────────────┐</span></span>
<span class="line"><span>                    │     Nginx       │</span></span>
<span class="line"><span>                    │   (443/80)      │</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>        │  前端静态  │  │  Gateway  │  │   Nacos   │</span></span>
<span class="line"><span>        │   文件    │  │   9000    │  │   8848    │</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>              │   IAM    │ │Suite │ │Plugin│</span></span>
<span class="line"><span>              │   5001   │ │ 5002 │ │ ...  │</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>              │  MySQL  │ │ Redis │ │ MinIO │</span></span>
<span class="line"><span>              │  3306   │ │ 6379  │ │ 9000  │</span></span>
<span class="line"><span>              └─────────┘ └───────┘ └───────┘</span></span></code></pre>
</div><h3 id="高可用集群架构" tabindex="-1">高可用集群架构 <a class="header-anchor" href="#高可用集群架构" aria-label="Permalink to &quot;高可用集群架构&quot;">&ZeroWidthSpace;</a></h3>
<p>适合中大型项目、生产环境：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>                         ┌──────────────┐</span></span>
<span class="line"><span>                         │   SLB/LB     │</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>        │  Nginx-1  │     │  Nginx-2  │     │  Nginx-3  │</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>        │ Gateway-1 │     │ Gateway-2 │     │ Gateway-3 │</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>                          │   Nacos   │</span></span>
<span class="line"><span>                          │  Cluster  │</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>  │ IAM × N   │           │ Suite × N │           │ Plugin × N│</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>        │MySQL主从  │     │Redis集群  │     │MinIO集群  │</span></span>
<span class="line"><span>        └───────────┘     └───────────┘     └───────────┘</span></span></code></pre>
</div><h2 id="生产级-docker-compose" tabindex="-1">生产级 Docker Compose <a class="header-anchor" href="#生产级-docker-compose" aria-label="Permalink to &quot;生产级 Docker Compose&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="目录结构" tabindex="-1">目录结构 <a class="header-anchor" href="#目录结构" aria-label="Permalink to &quot;目录结构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>/opt/wemirr/</span></span>
<span class="line"><span>├── docker-compose.yml</span></span>
<span class="line"><span>├── .env</span></span>
<span class="line"><span>├── nginx/</span></span>
<span class="line"><span>│   ├── nginx.conf</span></span>
<span class="line"><span>│   ├── conf.d/</span></span>
<span class="line"><span>│   │   └── default.conf</span></span>
<span class="line"><span>│   └── ssl/</span></span>
<span class="line"><span>│       ├── cert.pem</span></span>
<span class="line"><span>│       └── key.pem</span></span>
<span class="line"><span>├── mysql/</span></span>
<span class="line"><span>│   ├── conf/</span></span>
<span class="line"><span>│   │   └── my.cnf</span></span>
<span class="line"><span>│   └── init/</span></span>
<span class="line"><span>│       └── init.sql</span></span>
<span class="line"><span>├── redis/</span></span>
<span class="line"><span>│   └── redis.conf</span></span>
<span class="line"><span>├── logs/</span></span>
<span class="line"><span>└── data/</span></span></code></pre>
</div><h3 id="docker-compose-yml" tabindex="-1">docker-compose.yml <a class="header-anchor" href="#docker-compose-yml" aria-label="Permalink to &quot;docker-compose.yml&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'3.8'</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  wemirr-net</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    driver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">bridge</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nacos-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  minio-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  gateway-logs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  iam-logs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  suite-logs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">services</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # ==================== 中间件层 ====================</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql:8.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-mysql</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"3306:3306"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql-data:/var/lib/mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./mysql/init:/docker-entrypoint-initdb.d</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    command</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--character-set-server=utf8mb4</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--collation-server=utf8mb4_general_ci</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--lower_case_table_names=1</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--max_connections=1000</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">--innodb_buffer_pool_size=1G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">2G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    healthcheck</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CMD"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"mysqladmin"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ping"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-h"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"localhost"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-p${MYSQL_ROOT_PASSWORD}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      interval</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">10s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">5s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis:7-alpine</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"6379:6379"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis-data:/data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./redis/redis.conf:/usr/local/etc/redis/redis.conf</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    command</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis-server /usr/local/etc/redis/redis.conf</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">512M</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    healthcheck</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CMD"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"redis-cli"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-a"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"${REDIS_PASSWORD}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ping"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      interval</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">10s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">5s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos/nacos-server:v2.3.0</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-nacos</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8848:8848"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9848:9848"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9849:9849"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MODE=standalone</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_DATASOURCE_PLATFORM=mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_HOST=mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_PORT=3306</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_DB_NAME=nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_USER=root</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MYSQL_SERVICE_PASSWORD=${MYSQL_ROOT_PASSWORD}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_ENABLE=true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_TOKEN=${NACOS_AUTH_TOKEN}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_IDENTITY_KEY=serverIdentity</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_AUTH_IDENTITY_VALUE=${NACOS_IDENTITY_VALUE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JVM_XMS=512m</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JVM_XMX=1g</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos-data:/home/nacos/data</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        condition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">service_healthy</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  minio</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">bitnami/minio:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-minio</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"19000:9000"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"19001:9001"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MINIO_ROOT_USER=${MINIO_ROOT_USER}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">MINIO_DEFAULT_BUCKETS=wemirr-bucket</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio-data:/bitnami/minio/data</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">512M</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # ==================== 应用层 ====================</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-gateway:${APP_VERSION}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9000:9000"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_PROFILES_ACTIVE=prod</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_SERVER_ADDR=nacos:8848</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_NAMESPACE=${NACOS_NAMESPACE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">gateway-logs:/app/logs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    healthcheck</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CMD"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"curl"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"-f"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"http://localhost:9000/actuator/health"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      interval</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">30s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">10s</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  iam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-iam:${APP_VERSION}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"5001:5001"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_PROFILES_ACTIVE=prod</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_SERVER_ADDR=nacos:8848</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_NAMESPACE=${NACOS_NAMESPACE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">iam-logs:/app/logs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  suite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">registry.cn-hangzhou.aliyuncs.com/wemirr/wemirr-platform-suite:${APP_VERSION}</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-suite</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"5002:5002"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SPRING_PROFILES_ACTIVE=prod</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_SERVER_ADDR=nacos:8848</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">NACOS_NAMESPACE=${NACOS_NAMESPACE}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TZ=Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">JAVA_OPTS=-Xms512m -Xmx1g -XX:+UseG1GC</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">suite-logs:/app/logs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mysql</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    deploy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        limits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          memory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1.5G</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # ==================== 反向代理 ====================</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  nginx</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">nginx:alpine</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    container_name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-nginx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    networks</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-net</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"80:80"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"443:443"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/nginx.conf:/etc/nginx/nginx.conf</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/conf.d:/etc/nginx/conf.d</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/ssl:/etc/nginx/ssl</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./nginx/html:/usr/share/nginx/html</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./logs/nginx:/var/log/nginx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    depends_on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    restart</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">always</span></span></code></pre>
</div><h3 id="env-文件" tabindex="-1">.env 文件 <a class="header-anchor" href="#env-文件" aria-label="Permalink to &quot;.env 文件&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># MySQL</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_mysql_password_here</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Redis</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">REDIS_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_redis_password_here</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Nacos</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_AUTH_TOKEN</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">SecretKey012345678901234567890123456789012345678901234567890123456789</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_IDENTITY_VALUE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_identity_value</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">NACOS_NAMESPACE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">prod</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># MinIO</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_USER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minio_admin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MINIO_ROOT_PASSWORD</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">your_secure_minio_password_here</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 应用版本</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">APP_VERSION</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">v4.0.0</span></span></code></pre>
</div><h3 id="nginx-配置" tabindex="-1">Nginx 配置 <a class="header-anchor" href="#nginx-配置" aria-label="Permalink to &quot;Nginx 配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># nginx/conf.d/default.conf</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">upstream</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> gateway </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> gateway:9000;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    keepalive </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your-domain.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 301</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> https://$server_name$request_uri;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl http2;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your-domain.com;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/ssl/cert.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/ssl/key.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_session_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1d</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_session_cache </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">shared:SSL:50m;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_protocols </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TLSv1.2 TLSv1.3;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_ciphers </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_prefer_server_ciphers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 安全头</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Frame-Options </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"SAMEORIGIN"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> always;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Content-Type-Options </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"nosniff"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> always;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-XSS-Protection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"1; mode=block"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> always;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 前端静态资源</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/usr/share/nginx/html;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        index </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">index.html;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try_files </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$uri $uri/ /index.html;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 静态资源缓存</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        location</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ~*</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"> \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            expires </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30d</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Cache-Control </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"public, immutable"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # API 代理</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api/ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://gateway/;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_http_version </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Host $host;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Real-IP $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-Proto $scheme;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">""</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 超时设置</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_connect_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60s</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_send_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60s</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_read_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">60s</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # WebSocket 支持</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Upgrade $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 健康检查</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /health </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 200</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'OK'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        add_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Content-Type text/plain;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="使用仓库自带-nginx-配置-推荐先对齐" tabindex="-1">使用仓库自带 Nginx 配置（推荐先对齐） <a class="header-anchor" href="#使用仓库自带-nginx-配置-推荐先对齐" aria-label="Permalink to &quot;使用仓库自带 Nginx 配置（推荐先对齐）&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr 后端仓库在 <code>附件/nginx/</code> 目录提供了作者线上环境使用的 Nginx 配置，适合你在生产环境落地时参考并做二次调整：</p>
<ul>
<li><code>wemirr-platform/附件/nginx/nginx-http.conf</code></li>
<li><code>wemirr-platform/附件/nginx/nginx-https.conf</code></li>
</ul>
<blockquote>
<p>说明：配置中默认域名为 <code>cloud.battcn.com</code>，静态资源目录为 <code>/opt/wemirr-platform/cloud-ui/dist</code>，你需要替换为自己的域名与前端产物路径。</p>
</blockquote>
<h3 id="http-版本示例-来自仓库" tabindex="-1">HTTP 版本示例（来自仓库） <a class="header-anchor" href="#http-版本示例-来自仓库" aria-label="Permalink to &quot;HTTP 版本示例（来自仓库）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/opt/wemirr-platform/cloud-ui/dist;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        gzip_static </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api/ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:9000/;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_redirect </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Host $host;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Real-IP $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_http_version </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Upgrade $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_max_body_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_body_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_connect_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_send_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_read_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_busy_buffers_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_temp_file_write_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="https-版本示例-来自仓库" tabindex="-1">HTTPS 版本示例（来自仓库） <a class="header-anchor" href="#https-版本示例-来自仓库" aria-label="Permalink to &quot;HTTPS 版本示例（来自仓库）&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">      80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    rewrite</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF"> ^(.*)$</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  https://cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cloud.battcn.com;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 证书是从阿里云申请的免费证书</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /usr/local/nginx/conf/myconf/cert/cloud/4869096_cloud.battcn.com.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /usr/local/nginx/conf/myconf/cert/cloud/4869096_cloud.battcn.com.key;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_session_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_ciphers </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_protocols </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TLSv1 TLSv1.1 TLSv1.2;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_prefer_server_ciphers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/opt/wemirr-platform/cloud-ui/dist;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        gzip_static </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api/ </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:9000/;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_redirect </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Host $host;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Real-IP $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">X-Forwarded-For $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_http_version </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Upgrade $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_set_header </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Connection </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_max_body_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        client_body_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_connect_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_send_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_read_timeout </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffer_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_buffers </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_busy_buffers_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_temp_file_write_size </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="静态资源路径建议" tabindex="-1">静态资源路径建议 <a class="header-anchor" href="#静态资源路径建议" aria-label="Permalink to &quot;静态资源路径建议&quot;">&ZeroWidthSpace;</a></h3>
<p>配置中的 <code>root /opt/wemirr-platform/cloud-ui/dist;</code> 是作者线上路径。</p>
<p>如果你的前端是通过 <code>wemirr-platform-ui</code> 构建产物部署，建议将产物解压到类似目录：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>/opt/wemirr-platform/</span></span>
<span class="line"><span>└── cloud-ui/</span></span>
<span class="line"><span>    └── dist/</span></span></code></pre>
</div><p>并保持与 Nginx <code>root</code> 一致。</p>
<h2 id="生产部署检查清单" tabindex="-1">生产部署检查清单 <a class="header-anchor" href="#生产部署检查清单" aria-label="Permalink to &quot;生产部署检查清单&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-端口暴露" tabindex="-1">1. 端口暴露 <a class="header-anchor" href="#_1-端口暴露" aria-label="Permalink to &quot;1. 端口暴露&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>对外只暴露：<code>80/443</code></li>
<li><code>3306/6379/8848/15672</code> 等中间件端口：
<ul>
<li>建议只在内网访问</li>
<li>或通过防火墙限制来源 IP</li>
</ul>
</li>
</ul>
<h3 id="_2-数据持久化" tabindex="-1">2. 数据持久化 <a class="header-anchor" href="#_2-数据持久化" aria-label="Permalink to &quot;2. 数据持久化&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>MySQL：必须挂载 <code>/var/lib/mysql</code></li>
<li>Redis：建议开启 AOF 并挂载 <code>/data</code></li>
<li>Nacos：建议挂载 <code>/home/nacos/data</code></li>
</ul>
<h3 id="_3-备份与恢复演练" tabindex="-1">3. 备份与恢复演练 <a class="header-anchor" href="#_3-备份与恢复演练" aria-label="Permalink to &quot;3. 备份与恢复演练&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>至少保证：数据库备份 + Nacos 配置备份</li>
<li>建议每月至少做一次恢复演练</li>
</ul>
<h3 id="_4-配置与密钥" tabindex="-1">4. 配置与密钥 <a class="header-anchor" href="#_4-配置与密钥" aria-label="Permalink to &quot;4. 配置与密钥&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><code>.env</code> 与证书文件不要提交到仓库</li>
<li>Nacos Token、Redis 密码、MySQL 密码必须使用强密码</li>
</ul>
<h3 id="mysql-配置" tabindex="-1">MySQL 配置 <a class="header-anchor" href="#mysql-配置" aria-label="Permalink to &quot;MySQL 配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-ini vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># mysql/conf/my.cnf</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[mysqld]</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">character-set-server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=utf8mb4</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">collation-server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=utf8mb4_general_ci</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">lower_case_table_names</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 连接数</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">max_connections</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1000</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">max_connect_errors</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=100</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># InnoDB</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_buffer_pool_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1G</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_log_file_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=256M</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_flush_log_at_trx_commit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=2</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">innodb_flush_method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=O_DIRECT</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 日志</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">slow_query_log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">slow_query_log_file</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=/var/lib/mysql/slow.log</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">long_query_time</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=2</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 二进制日志（主从复制需要）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server-id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=1</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">log_bin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=mysql-bin</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">binlog_format</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=ROW</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">expire_logs_days</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=7</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[client]</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default-character-set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=utf8mb4</span></span></code></pre>
</div><h3 id="redis-配置" tabindex="-1">Redis 配置 <a class="header-anchor" href="#redis-配置" aria-label="Permalink to &quot;Redis 配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-conf vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">conf</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span># redis/redis.conf</span></span>
<span class="line"><span>bind 0.0.0.0</span></span>
<span class="line"><span>port 6379</span></span>
<span class="line"><span>requirepass your_secure_redis_password_here</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 持久化</span></span>
<span class="line"><span>appendonly yes</span></span>
<span class="line"><span>appendfsync everysec</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 内存管理</span></span>
<span class="line"><span>maxmemory 512mb</span></span>
<span class="line"><span>maxmemory-policy allkeys-lru</span></span>
<span class="line"><span></span></span>
<span class="line"><span># 安全</span></span>
<span class="line"><span>rename-command FLUSHALL ""</span></span>
<span class="line"><span>rename-command FLUSHDB ""</span></span>
<span class="line"><span>rename-command CONFIG ""</span></span></code></pre>
</div><h2 id="部署流程" tabindex="-1">部署流程 <a class="header-anchor" href="#部署流程" aria-label="Permalink to &quot;部署流程&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-准备服务器" tabindex="-1">1. 准备服务器 <a class="header-anchor" href="#_1-准备服务器" aria-label="Permalink to &quot;1. 准备服务器&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 更新系统</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> upgrade</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -y</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装 Docker</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -fsSL</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://get.docker.com</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sh</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> systemctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装 Docker Compose</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -L</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "https://github.com/docker/compose/releases/latest/download/docker-compose-$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">uname</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)-$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">uname</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -o</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /usr/local/bin/docker-compose</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> chmod</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +x</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /usr/local/bin/docker-compose</span></span></code></pre>
</div><h3 id="_2-部署中间件" tabindex="-1">2. 部署中间件 <a class="header-anchor" href="#_2-部署中间件" aria-label="Permalink to &quot;2. 部署中间件&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动中间件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> minio</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 等待服务就绪</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sleep</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 30</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查服务状态</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span></span></code></pre>
</div><h3 id="_3-初始化数据" tabindex="-1">3. 初始化数据 <a class="header-anchor" href="#_3-初始化数据" aria-label="Permalink to &quot;3. 初始化数据&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 导入数据库</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -uroot</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> &#x3C;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ./mysql/init/v4-dev.sql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 导入 Nacos 配置（通过 Web 界面或 API）</span></span></code></pre>
</div><h3 id="_4-部署应用" tabindex="-1">4. 部署应用 <a class="header-anchor" href="#_4-部署应用" aria-label="Permalink to &quot;4. 部署应用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动所有应用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gateway</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> iam</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> suite</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nginx</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gateway</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> iam</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> suite</span></span></code></pre>
</div><h3 id="_5-验证部署" tabindex="-1">5. 验证部署 <a class="header-anchor" href="#_5-验证部署" aria-label="Permalink to &quot;5. 验证部署&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 健康检查</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost/health</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -s</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost:9000/actuator/health</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 访问系统</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">echo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "请访问: https://your-domain.com"</span></span></code></pre>
</div><h2 id="运维管理" tabindex="-1">运维管理 <a class="header-anchor" href="#运维管理" aria-label="Permalink to &quot;运维管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="日志管理" tabindex="-1">日志管理 <a class="header-anchor" href="#日志管理" aria-label="Permalink to &quot;日志管理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看实时日志</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -f</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --tail=100</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> gateway</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 日志轮转配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">cat</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /etc/docker/daemon.json</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> &#x3C;&#x3C;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> EOF</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">{</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "log-driver": "json-file",</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  "log-opts": {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "max-size": "100m",</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">    "max-file": "3"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  }</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">EOF</span></span></code></pre>
</div><h3 id="监控告警" tabindex="-1">监控告警 <a class="header-anchor" href="#监控告警" aria-label="Permalink to &quot;监控告警&quot;">&ZeroWidthSpace;</a></h3>
<p>推荐使用 Prometheus + Grafana 进行监控：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 添加到 docker-compose.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">prometheus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">prom/prometheus:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  volumes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"9090:9090"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">grafana</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">grafana/grafana:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  ports</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"3000:3000"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">GF_SECURITY_ADMIN_PASSWORD=admin123</span></span></code></pre>
</div><h3 id="备份策略" tabindex="-1">备份策略 <a class="header-anchor" href="#备份策略" aria-label="Permalink to &quot;备份策略&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">#!/bin/bash</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># backup.sh</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BACKUP_DIR</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/opt/wemirr/backup/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">date</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +%Y%m%d</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 备份 MySQL</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-mysql</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysqldump</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -uroot</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">MYSQL_ROOT_PASSWORD</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">}</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --all-databases</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/mysql.sql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 备份 Redis</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-redis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis-cli</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ${REDIS_PASSWORD} </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BGSAVE</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> cp</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-redis:/data/dump.rdb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 备份配置文件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">cp</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -r</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/.env</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/nginx</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $BACKUP_DIR</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 清理7天前的备份</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">find</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/backup</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -type</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mtime</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +7</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -exec</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> +</span></span></code></pre>
</div><p>添加到 crontab：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">0</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> *</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr/backup.sh</span></span></code></pre>
</div><h2 id="安全加固" tabindex="-1">安全加固 <a class="header-anchor" href="#安全加固" aria-label="Permalink to &quot;安全加固&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-防火墙配置" tabindex="-1">1. 防火墙配置 <a class="header-anchor" href="#_1-防火墙配置" aria-label="Permalink to &quot;1. 防火墙配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 只开放必要端口</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> allow</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 80/tcp</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> allow</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 443/tcp</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> allow</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 22/tcp</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ufw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span></span></code></pre>
</div><h3 id="_2-密码强度" tabindex="-1">2. 密码强度 <a class="header-anchor" href="#_2-密码强度" aria-label="Permalink to &quot;2. 密码强度&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>MySQL 密码至少 16 位，包含大小写字母、数字、特殊字符</li>
<li>Redis 密码至少 32 位</li>
<li>Nacos Token 至少 64 位</li>
</ul>
<h3 id="_3-定期更新" tabindex="-1">3. 定期更新 <a class="header-anchor" href="#_3-定期更新" aria-label="Permalink to &quot;3. 定期更新&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 更新镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/packages/devops/monitor.html">监控中心</a> - 配置系统监控</li>
<li><a href="/zh/guide/packages/devops/nginx.html">Nginx 配置</a> - 深入了解 Nginx 配置</li>
<li><a href="/zh/faq/">故障排查</a> - 常见问题解决</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[环境部署 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/deploy.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/deploy.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="环境部署" tabindex="-1">环境部署 <a class="header-anchor" href="#环境部署" aria-label="Permalink to &quot;环境部署&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="后端体系" tabindex="-1">后端体系 <a class="header-anchor" href="#后端体系" aria-label="Permalink to &quot;后端体系&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">采用技术</p>
<p>JDK17 <a href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html" target="_blank" rel="noreferrer">https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html</a></p>
<p>GIT <a href="https://git-scm.com/" target="_blank" rel="noreferrer">https://git-scm.com/</a></p>
<p>Maven <a href="https://maven.apache.org/" target="_blank" rel="noreferrer">https://maven.apache.org/</a></p>
<p>Spring Boot <a href="https://spring.io/projects/spring-boot" target="_blank" rel="noreferrer">https://spring.io/projects/spring-boot</a></p>
<p>Spring Cloud  <a href="https://spring.io/projects/spring-cloud" target="_blank" rel="noreferrer">https://spring.io/projects/spring-cloud</a></p>
</div>
<h3 id="获取代码" tabindex="-1">获取代码 <a class="header-anchor" href="#获取代码" aria-label="Permalink to &quot;获取代码&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 不能访问 GITHUB 的可以采用 GITEE 镜像库</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://github.com/battcn/wemirr-platform.git</span></span></code></pre>
</div><p>前往 GIT 下载页面 <a href="https://gitee.com/battcn/wemirr-platform" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform</a> 下载解压到工作目录</p>
<p>将项目导入到Idea，菜单 File -&gt; Open，选择工作目录，然后点击 Open 按钮，即可成功导入。 IDEA 会自动加载Maven依赖包，初次加载会比较慢（根据自身网络情况而定）</p>
<h3 id="初始数据" tabindex="-1">初始数据 <a class="header-anchor" href="#初始数据" aria-label="Permalink to &quot;初始数据&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>执行项目 <strong>附件/mysql（文件夹）</strong> 提供的SQL脚本，将数据初始到您之前安装好的数据库中</p>
</div>
<h3 id="导入配置" tabindex="-1">导入配置 <a class="header-anchor" href="#导入配置" aria-label="Permalink to &quot;导入配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<ul>
<li>登录 Nacos 控制台 <a href="http://ip" target="_blank" rel="noreferrer">http://ip</a>:port/nacos</li>
<li>创建命名空间 <code>v4-dev</code> （如果导入到 public 下,需要注释 <code>application.yml</code> 的 <code>namespace</code> 配置）</li>
<li>执行项目 <strong>附件/nacos（文件夹）</strong> 提供配置，将 ZIP 包直接导入到您之前安装好的 <code>Nacos</code> 中
<img src="/env/nacos-namespace.png" alt="命名空间"></li>
</ul>
</div>
<h3 id="导入项目" tabindex="-1">导入项目 <a class="header-anchor" href="#导入项目" aria-label="Permalink to &quot;导入项目&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意事项</p>
<p>如果您导入的项目不长这样，或者右侧 Maven 处没内容</p>
<p>将 <code>wemirr-platform-dependencies</code>、<code>wemirr-platform-framework</code> 模块中的 <strong><code>pom.xml</code> 右键 <code>Add As Maven Project</code></strong> 然后将它们 <code>install</code> 到本地 Maven 仓库</p>
</div>
<p><img src="/back/img-1.png" alt="黑白名单"></p>
<h3 id="运行演示" tabindex="-1">运行演示 <a class="header-anchor" href="#运行演示" aria-label="Permalink to &quot;运行演示&quot;">&ZeroWidthSpace;</a></h3>
<p><strong><code>wemirr-plugin</code> 目录中的项目请按需使用,用不上的可以自行忽略或者删除</strong></p>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<ul>
<li>GatewayApplication.java     端口 9000</li>
<li>IamApplication.java         端口 5001</li>
<li>SuiteApplication.java       端口 5002</li>
</ul>
</div>
<h2 id="前端体系" tabindex="-1">前端体系 <a class="header-anchor" href="#前端体系" aria-label="Permalink to &quot;前端体系&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">采用技术</p>
<ul>
<li>Node <a href="http://nodejs.org/" target="_blank" rel="noreferrer">http://nodejs.org/</a></li>
<li>Vite <a href="https://vitejs.dev/" target="_blank" rel="noreferrer">https://vitejs.dev/</a>  熟悉 vite 特性</li>
<li>Vue3 <a href="https://v3.vuejs.org/" target="_blank" rel="noreferrer">https://v3.vuejs.org/</a>  熟悉 Vue 基础语法</li>
<li>TypeScript <a href="https://www.typescriptlang.org/" target="_blank" rel="noreferrer">https://www.typescriptlang.org/</a>  熟悉<code>TypeScript</code>基本语法</li>
<li>Es6+ <a href="http://es6.ruanyifeng.com/" target="_blank" rel="noreferrer">http://es6.ruanyifeng.com/</a>  熟悉 es6 基本语法</li>
<li>Vue-Router-Next <a href="https://next.router.vuejs.org/" target="_blank" rel="noreferrer">https://next.router.vuejs.org/</a>  熟悉 vue-router 基本使用</li>
<li>Ant-Design-Vue  <a href="https://www.antdv.com/" target="_blank" rel="noreferrer">https://www.antdv.com/</a>  ui 基本使用</li>
</ul>
</div>
<h3 id="环境安装" tabindex="-1">环境安装 <a class="header-anchor" href="#环境安装" aria-label="Permalink to &quot;环境安装&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意事项</p>
<p>没有安装 node 的朋友请自行百度解决,该页暂不提供
<a href="https://www.runoob.com/nodejs/nodejs-install-setup.html" target="_blank" rel="noreferrer">Node 安装文档</a></p>
</div>
<h3 id="获取代码-1" tabindex="-1">获取代码 <a class="header-anchor" href="#获取代码-1" aria-label="Permalink to &quot;获取代码&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>获取项目代码</li>
</ul>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 不能访问 GITHUB 的可以采用 GITEE 镜像库</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://github.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div><h3 id="安装依赖" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖" aria-label="Permalink to &quot;安装依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-ui</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果没有 pnpm 请先安装 pnpm</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># npm install -g pnpm</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span></code></pre>
</div><h3 id="运行演示-1" tabindex="-1">运行演示 <a class="header-anchor" href="#运行演示-1" aria-label="Permalink to &quot;运行演示&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果您已成功运行了配套后端</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><h3 id="打包部署" tabindex="-1">打包部署 <a class="header-anchor" href="#打包部署" aria-label="Permalink to &quot;打包部署&quot;">&ZeroWidthSpace;</a></h3>
<blockquote>
<p>注意事项:关闭 GITEE 授权拦截</p>
</blockquote>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span># GITEE 授权拦截,开启后默认会进行 GITEE OAUTH2.0 授权检验,这个地方主要是给演示使用,FORK代码后可以删除或者关闭</span></span>
<span class="line"><span>VITE_GITEE_INTERCEPT=false</span></span></code></pre>
</div><blockquote>
<p>打包指令</p>
</blockquote>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 打包全部工程</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 打包 antdv 版本的包（ 99.999% 情况下你只需要打 antdv 版本）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build:antd</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="环境部署" tabindex="-1">环境部署 <a class="header-anchor" href="#环境部署" aria-label="Permalink to &quot;环境部署&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="后端体系" tabindex="-1">后端体系 <a class="header-anchor" href="#后端体系" aria-label="Permalink to &quot;后端体系&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">采用技术</p>
<p>JDK17 <a href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html" target="_blank" rel="noreferrer">https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html</a></p>
<p>GIT <a href="https://git-scm.com/" target="_blank" rel="noreferrer">https://git-scm.com/</a></p>
<p>Maven <a href="https://maven.apache.org/" target="_blank" rel="noreferrer">https://maven.apache.org/</a></p>
<p>Spring Boot <a href="https://spring.io/projects/spring-boot" target="_blank" rel="noreferrer">https://spring.io/projects/spring-boot</a></p>
<p>Spring Cloud  <a href="https://spring.io/projects/spring-cloud" target="_blank" rel="noreferrer">https://spring.io/projects/spring-cloud</a></p>
</div>
<h3 id="获取代码" tabindex="-1">获取代码 <a class="header-anchor" href="#获取代码" aria-label="Permalink to &quot;获取代码&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 不能访问 GITHUB 的可以采用 GITEE 镜像库</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://github.com/battcn/wemirr-platform.git</span></span></code></pre>
</div><p>前往 GIT 下载页面 <a href="https://gitee.com/battcn/wemirr-platform" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform</a> 下载解压到工作目录</p>
<p>将项目导入到Idea，菜单 File -&gt; Open，选择工作目录，然后点击 Open 按钮，即可成功导入。 IDEA 会自动加载Maven依赖包，初次加载会比较慢（根据自身网络情况而定）</p>
<h3 id="初始数据" tabindex="-1">初始数据 <a class="header-anchor" href="#初始数据" aria-label="Permalink to &quot;初始数据&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>执行项目 <strong>附件/mysql（文件夹）</strong> 提供的SQL脚本，将数据初始到您之前安装好的数据库中</p>
</div>
<h3 id="导入配置" tabindex="-1">导入配置 <a class="header-anchor" href="#导入配置" aria-label="Permalink to &quot;导入配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<ul>
<li>登录 Nacos 控制台 <a href="http://ip" target="_blank" rel="noreferrer">http://ip</a>:port/nacos</li>
<li>创建命名空间 <code>v4-dev</code> （如果导入到 public 下,需要注释 <code>application.yml</code> 的 <code>namespace</code> 配置）</li>
<li>执行项目 <strong>附件/nacos（文件夹）</strong> 提供配置，将 ZIP 包直接导入到您之前安装好的 <code>Nacos</code> 中
<img src="/env/nacos-namespace.png" alt="命名空间"></li>
</ul>
</div>
<h3 id="导入项目" tabindex="-1">导入项目 <a class="header-anchor" href="#导入项目" aria-label="Permalink to &quot;导入项目&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意事项</p>
<p>如果您导入的项目不长这样，或者右侧 Maven 处没内容</p>
<p>将 <code>wemirr-platform-dependencies</code>、<code>wemirr-platform-framework</code> 模块中的 <strong><code>pom.xml</code> 右键 <code>Add As Maven Project</code></strong> 然后将它们 <code>install</code> 到本地 Maven 仓库</p>
</div>
<p><img src="/back/img-1.png" alt="黑白名单"></p>
<h3 id="运行演示" tabindex="-1">运行演示 <a class="header-anchor" href="#运行演示" aria-label="Permalink to &quot;运行演示&quot;">&ZeroWidthSpace;</a></h3>
<p><strong><code>wemirr-plugin</code> 目录中的项目请按需使用,用不上的可以自行忽略或者删除</strong></p>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<ul>
<li>GatewayApplication.java     端口 9000</li>
<li>IamApplication.java         端口 5001</li>
<li>SuiteApplication.java       端口 5002</li>
</ul>
</div>
<h2 id="前端体系" tabindex="-1">前端体系 <a class="header-anchor" href="#前端体系" aria-label="Permalink to &quot;前端体系&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">采用技术</p>
<ul>
<li>Node <a href="http://nodejs.org/" target="_blank" rel="noreferrer">http://nodejs.org/</a></li>
<li>Vite <a href="https://vitejs.dev/" target="_blank" rel="noreferrer">https://vitejs.dev/</a>  熟悉 vite 特性</li>
<li>Vue3 <a href="https://v3.vuejs.org/" target="_blank" rel="noreferrer">https://v3.vuejs.org/</a>  熟悉 Vue 基础语法</li>
<li>TypeScript <a href="https://www.typescriptlang.org/" target="_blank" rel="noreferrer">https://www.typescriptlang.org/</a>  熟悉<code>TypeScript</code>基本语法</li>
<li>Es6+ <a href="http://es6.ruanyifeng.com/" target="_blank" rel="noreferrer">http://es6.ruanyifeng.com/</a>  熟悉 es6 基本语法</li>
<li>Vue-Router-Next <a href="https://next.router.vuejs.org/" target="_blank" rel="noreferrer">https://next.router.vuejs.org/</a>  熟悉 vue-router 基本使用</li>
<li>Ant-Design-Vue  <a href="https://www.antdv.com/" target="_blank" rel="noreferrer">https://www.antdv.com/</a>  ui 基本使用</li>
</ul>
</div>
<h3 id="环境安装" tabindex="-1">环境安装 <a class="header-anchor" href="#环境安装" aria-label="Permalink to &quot;环境安装&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意事项</p>
<p>没有安装 node 的朋友请自行百度解决,该页暂不提供
<a href="https://www.runoob.com/nodejs/nodejs-install-setup.html" target="_blank" rel="noreferrer">Node 安装文档</a></p>
</div>
<h3 id="获取代码-1" tabindex="-1">获取代码 <a class="header-anchor" href="#获取代码-1" aria-label="Permalink to &quot;获取代码&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>获取项目代码</li>
</ul>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 不能访问 GITHUB 的可以采用 GITEE 镜像库</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://github.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div><h3 id="安装依赖" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖" aria-label="Permalink to &quot;安装依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-ui</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果没有 pnpm 请先安装 pnpm</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># npm install -g pnpm</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span></code></pre>
</div><h3 id="运行演示-1" tabindex="-1">运行演示 <a class="header-anchor" href="#运行演示-1" aria-label="Permalink to &quot;运行演示&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果您已成功运行了配套后端</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><h3 id="打包部署" tabindex="-1">打包部署 <a class="header-anchor" href="#打包部署" aria-label="Permalink to &quot;打包部署&quot;">&ZeroWidthSpace;</a></h3>
<blockquote>
<p>注意事项:关闭 GITEE 授权拦截</p>
</blockquote>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span># GITEE 授权拦截,开启后默认会进行 GITEE OAUTH2.0 授权检验,这个地方主要是给演示使用,FORK代码后可以删除或者关闭</span></span>
<span class="line"><span>VITE_GITEE_INTERCEPT=false</span></span></code></pre>
</div><blockquote>
<p>打包指令</p>
</blockquote>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 打包全部工程</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 打包 antdv 版本的包（ 99.999% 情况下你只需要打 antdv 版本）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build:antd</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[环境安装 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/install.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/install.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="环境安装" tabindex="-1">环境安装 <a class="header-anchor" href="#环境安装" aria-label="Permalink to &quot;环境安装&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">须知</p>
<p>一般安装 <code>latest</code> 版本即可，也可以自行指定版本 <code>docker search</code> 或者自己上 <code>docker hub</code> 看版本</p>
<p>如果 <code>docker</code> 运行开发环境，建议先创建一个网络 <code>docker network create wemirr</code> 后面容器都走统一网络</p>
<p><strong>提供 <code>docker-compose.yml</code> 安装（文件在<code>附件/docker/docker-compose.yml</code>）</strong></p>
</div>
<h2 id="必要组件" tabindex="-1">必要组件 <a class="header-anchor" href="#必要组件" aria-label="Permalink to &quot;必要组件&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>创建网络: <strong><code>docker run --net wemirr --name xxx</code></strong></p>
</div>
<p><strong>最小中间件依赖，<code>数据库</code>、<code>缓存</code>,<code>Naocs</code></strong> 当然你也可以切换其它注册中心或者配置中心,这样需要开发者自己去修改了</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=3.0）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:latest</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5.7）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:latest</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=2.0）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建Nacos容器，注意开放9848及9848，原因：https://nacos.io/en/docs/next/v2/upgrading/2.0.0-compatibility#deployment</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart=always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span></code></pre>
</div><h2 id="可选组件" tabindex="-1">可选组件 <a class="header-anchor" href="#可选组件" aria-label="Permalink to &quot;可选组件&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>可选组件是非必要的,如果没必要就不用安装了,一般企业用不到</p>
</div>
<h3 id="消息总线-动态数据源租户" tabindex="-1">消息总线（动态数据源租户） <a class="header-anchor" href="#消息总线-动态数据源租户" aria-label="Permalink to &quot;消息总线（动态数据源租户）&quot;">&ZeroWidthSpace;</a></h3>
<p><em>多租户 =&gt; 动态数据源之间同步用到了消息总线</em> 本项目默认使用 <code>RabbitMQ</code> 做消息总线和消息队列，如与公司中间件不符可自行修改</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> macintoshplus/rabbitmq-management</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15671:15671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  macintoshplus/rabbitmq-management</span></span></code></pre>
</div><h3 id="文件存储" tabindex="-1">文件存储 <a class="header-anchor" href="#文件存储" aria-label="Permalink to &quot;文件存储&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bitnami/minio:latest</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建MinIO容器，密码必须大于等于8个字节，否则启动失败；初始化一个默认bucket为wemirr-bucket；9000端口与Gateway冲突，两个端口偏移10000</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> minio</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 19000:9000</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 19001:9001</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MINIO_ROOT_USER=minio</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MINIO_ROOT_PASSWORD=minio_pwd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MINIO_DEFAULT_BUCKETS=wemirr-bucket</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bitnami/minio:latest</span></span></code></pre>
</div><h3 id="流量控制" tabindex="-1">流量控制 <a class="header-anchor" href="#流量控制" aria-label="Permalink to &quot;流量控制&quot;">&ZeroWidthSpace;</a></h3>
<p>默认使用 <code>Spring Cloud Alibaba</code> 做流量保护，也可自行切换成 <code>Hystrix</code></p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bladex/sentinel-dashboard</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8858:8858</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8719:8719</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  bladex/sentinel-dashboard</span></span></code></pre>
</div><h3 id="链路追踪" tabindex="-1">链路追踪 <a class="header-anchor" href="#链路追踪" aria-label="Permalink to &quot;链路追踪&quot;">&ZeroWidthSpace;</a></h3>
<p>安装 skywalking-es7（<strong>8.7.0 有问题,别安装哦</strong>）</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果已执行请忽略</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9200:9200</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9300:9300</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "discovery.type=single-node"</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 1234:1234</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 12800:12800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 11800:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE=elasticsearch7</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap-ui</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10086:8080</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TZ=Asia/Shanghai</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_OAP_ADDRESS=oap:12800</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span></code></pre>
</div><h4 id="idea-配置" tabindex="-1">IDEA 配置 <a class="header-anchor" href="#idea-配置" aria-label="Permalink to &quot;IDEA 配置&quot;">&ZeroWidthSpace;</a></h4>
<p>通过IDEA提供的参数配置功能，配置我们的 <code>skywalking</code> 监控插件</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">VmOption</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/Users/battcn/Desktop/apache-skywalking-apm-bin/agent/skywalking-agent.jar</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-iam</span></span></code></pre>
</div><ul>
<li>启动命令</li>
</ul>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-gateway</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-gateway.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_gateway.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-iam</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-iam.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --spring.profiles.active=demo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_iam.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="环境安装" tabindex="-1">环境安装 <a class="header-anchor" href="#环境安装" aria-label="Permalink to &quot;环境安装&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">须知</p>
<p>一般安装 <code>latest</code> 版本即可，也可以自行指定版本 <code>docker search</code> 或者自己上 <code>docker hub</code> 看版本</p>
<p>如果 <code>docker</code> 运行开发环境，建议先创建一个网络 <code>docker network create wemirr</code> 后面容器都走统一网络</p>
<p><strong>提供 <code>docker-compose.yml</code> 安装（文件在<code>附件/docker/docker-compose.yml</code>）</strong></p>
</div>
<h2 id="必要组件" tabindex="-1">必要组件 <a class="header-anchor" href="#必要组件" aria-label="Permalink to &quot;必要组件&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>创建网络: <strong><code>docker run --net wemirr --name xxx</code></strong></p>
</div>
<p><strong>最小中间件依赖，<code>数据库</code>、<code>缓存</code>,<code>Naocs</code></strong> 当然你也可以切换其它注册中心或者配置中心,这样需要开发者自己去修改了</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Redis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=3.0）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:latest</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5.7）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:latest</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=2.0）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建Nacos容器，注意开放9848及9848，原因：https://nacos.io/en/docs/next/v2/upgrading/2.0.0-compatibility#deployment</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart=always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span></code></pre>
</div><h2 id="可选组件" tabindex="-1">可选组件 <a class="header-anchor" href="#可选组件" aria-label="Permalink to &quot;可选组件&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>可选组件是非必要的,如果没必要就不用安装了,一般企业用不到</p>
</div>
<h3 id="消息总线-动态数据源租户" tabindex="-1">消息总线（动态数据源租户） <a class="header-anchor" href="#消息总线-动态数据源租户" aria-label="Permalink to &quot;消息总线（动态数据源租户）&quot;">&ZeroWidthSpace;</a></h3>
<p><em>多租户 =&gt; 动态数据源之间同步用到了消息总线</em> 本项目默认使用 <code>RabbitMQ</code> 做消息总线和消息队列，如与公司中间件不符可自行修改</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> macintoshplus/rabbitmq-management</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15671:15671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  macintoshplus/rabbitmq-management</span></span></code></pre>
</div><h3 id="文件存储" tabindex="-1">文件存储 <a class="header-anchor" href="#文件存储" aria-label="Permalink to &quot;文件存储&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bitnami/minio:latest</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建MinIO容器，密码必须大于等于8个字节，否则启动失败；初始化一个默认bucket为wemirr-bucket；9000端口与Gateway冲突，两个端口偏移10000</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> minio</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 19000:9000</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 19001:9001</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MINIO_ROOT_USER=minio</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MINIO_ROOT_PASSWORD=minio_pwd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MINIO_DEFAULT_BUCKETS=wemirr-bucket</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bitnami/minio:latest</span></span></code></pre>
</div><h3 id="流量控制" tabindex="-1">流量控制 <a class="header-anchor" href="#流量控制" aria-label="Permalink to &quot;流量控制&quot;">&ZeroWidthSpace;</a></h3>
<p>默认使用 <code>Spring Cloud Alibaba</code> 做流量保护，也可自行切换成 <code>Hystrix</code></p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bladex/sentinel-dashboard</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -t</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8858:8858</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8719:8719</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  bladex/sentinel-dashboard</span></span></code></pre>
</div><h3 id="链路追踪" tabindex="-1">链路追踪 <a class="header-anchor" href="#链路追踪" aria-label="Permalink to &quot;链路追踪&quot;">&ZeroWidthSpace;</a></h3>
<p>安装 skywalking-es7（<strong>8.7.0 有问题,别安装哦</strong>）</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果已执行请忽略</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9200:9200</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9300:9300</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "discovery.type=single-node"</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 1234:1234</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 12800:12800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 11800:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE=elasticsearch7</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap-ui</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10086:8080</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TZ=Asia/Shanghai</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_OAP_ADDRESS=oap:12800</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span></code></pre>
</div><h4 id="idea-配置" tabindex="-1">IDEA 配置 <a class="header-anchor" href="#idea-配置" aria-label="Permalink to &quot;IDEA 配置&quot;">&ZeroWidthSpace;</a></h4>
<p>通过IDEA提供的参数配置功能，配置我们的 <code>skywalking</code> 监控插件</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">VmOption</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/Users/battcn/Desktop/apache-skywalking-apm-bin/agent/skywalking-agent.jar</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-iam</span></span></code></pre>
</div><ul>
<li>启动命令</li>
</ul>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-gateway</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-gateway.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_gateway.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-iam</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-iam.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --spring.profiles.active=demo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_iam.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[消息总线 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/middleware/bus.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/middleware/bus.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="消息总线" tabindex="-1">消息总线 <a class="header-anchor" href="#消息总线" aria-label="Permalink to &quot;消息总线&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>租户 <code>数据源</code> 同步依赖消息总线，未用 <code>租户-数据源隔离策略</code> 公司也没MQ需要可以跳过,无需安装</p>
<p><code>WEMIRR-PLATFORM</code> 默认采用的是 <code>RabbitMQ</code> 但只要是 <code>Spring Cloud Bus</code> 支持的就没有问题</p>
</div>
<h2 id="安装方式" tabindex="-1">安装方式 <a class="header-anchor" href="#安装方式" aria-label="Permalink to &quot;安装方式&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="docker" tabindex="-1">Docker <a class="header-anchor" href="#docker" aria-label="Permalink to &quot;Docker&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装带 management 的 mq </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> macintoshplus/rabbitmq-management</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 运行 MQ  </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15671:15671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  macintoshplus/rabbitmq-management</span></span></code></pre>
</div><h3 id="常规方式" tabindex="-1">常规方式 <a class="header-anchor" href="#常规方式" aria-label="Permalink to &quot;常规方式&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://baidu.com" target="_blank" rel="noreferrer">🔍 百度 - 请自行搜索</a></p>
<p><a href="https://www.google.com.hk/" target="_blank" rel="noreferrer">🔍 谷歌 - 请自行搜索</a></p>
]]></description>
            <content:encoded><![CDATA[<h1 id="消息总线" tabindex="-1">消息总线 <a class="header-anchor" href="#消息总线" aria-label="Permalink to &quot;消息总线&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>租户 <code>数据源</code> 同步依赖消息总线，未用 <code>租户-数据源隔离策略</code> 公司也没MQ需要可以跳过,无需安装</p>
<p><code>WEMIRR-PLATFORM</code> 默认采用的是 <code>RabbitMQ</code> 但只要是 <code>Spring Cloud Bus</code> 支持的就没有问题</p>
</div>
<h2 id="安装方式" tabindex="-1">安装方式 <a class="header-anchor" href="#安装方式" aria-label="Permalink to &quot;安装方式&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="docker" tabindex="-1">Docker <a class="header-anchor" href="#docker" aria-label="Permalink to &quot;Docker&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 安装带 management 的 mq </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> macintoshplus/rabbitmq-management</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 运行 MQ  </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15671:15671</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  macintoshplus/rabbitmq-management</span></span></code></pre>
</div><h3 id="常规方式" tabindex="-1">常规方式 <a class="header-anchor" href="#常规方式" aria-label="Permalink to &quot;常规方式&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://baidu.com" target="_blank" rel="noreferrer">🔍 百度 - 请自行搜索</a></p>
<p><a href="https://www.google.com.hk/" target="_blank" rel="noreferrer">🔍 谷歌 - 请自行搜索</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[数据存储 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/middleware/db.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/middleware/db.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="数据存储" tabindex="-1">数据存储 <a class="header-anchor" href="#数据存储" aria-label="Permalink to &quot;数据存储&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>WEMIRR-PLATFORM 默认采用的是 <code>mysql</code> 作为数据存储，支持任意的数据库，自己按需更换连接驱动即可</p>
</div>
<h2 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">&ZeroWidthSpace;</a></h2>
<p>中间件基本上选最新的就行，版本越新功能越强大，性能越高</p>
<h3 id="docker-方式" tabindex="-1">Docker 方式 <a class="header-anchor" href="#docker-方式" aria-label="Permalink to &quot;Docker 方式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5.7）</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span></code></pre>
</div><h3 id="常规方式" tabindex="-1">常规方式 <a class="header-anchor" href="#常规方式" aria-label="Permalink to &quot;常规方式&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://baidu.com" target="_blank" rel="noreferrer">🔍 百度 - 请自行搜索</a></p>
<p><a href="https://www.google.com.hk/" target="_blank" rel="noreferrer">🔍 谷歌 - 请自行搜索</a></p>
]]></description>
            <content:encoded><![CDATA[<h1 id="数据存储" tabindex="-1">数据存储 <a class="header-anchor" href="#数据存储" aria-label="Permalink to &quot;数据存储&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>WEMIRR-PLATFORM 默认采用的是 <code>mysql</code> 作为数据存储，支持任意的数据库，自己按需更换连接驱动即可</p>
</div>
<h2 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">&ZeroWidthSpace;</a></h2>
<p>中间件基本上选最新的就行，版本越新功能越强大，性能越高</p>
<h3 id="docker-方式" tabindex="-1">Docker 方式 <a class="header-anchor" href="#docker-方式" aria-label="Permalink to &quot;Docker 方式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">安装</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> （</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5.7）</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span></span></code></pre>
</div><h3 id="常规方式" tabindex="-1">常规方式 <a class="header-anchor" href="#常规方式" aria-label="Permalink to &quot;常规方式&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://baidu.com" target="_blank" rel="noreferrer">🔍 百度 - 请自行搜索</a></p>
<p><a href="https://www.google.com.hk/" target="_blank" rel="noreferrer">🔍 谷歌 - 请自行搜索</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[服务发现 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/middleware/discovery.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/middleware/discovery.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="服务发现" tabindex="-1">服务发现 <a class="header-anchor" href="#服务发现" aria-label="Permalink to &quot;服务发现&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h2>
<blockquote>
<p><strong><code>什么是注册中心</code></strong></p>
</blockquote>
<p>注册中心在微服务项目中扮演着非常重要的角色，是微服务架构中的纽带，类似于通讯录，它记录了服务和服务地址的映射关系。在分布式架构中，服务会注册到这里，当服务需要调用其它服务时，就到这里找到服务的地址，进行调用。</p>
<ul>
<li><strong><code>为什么要使用注册中心</code></strong></li>
</ul>
<p>解决了服务发现的问题。在没有注册中心时候，服务间调用需要知道被调方的地址或者代理地址。当服务更换部署地址，就不得不修改调用当中指定的地址或者修改代理配置。而有了注册中心之后，每个服务在调用别人的时候只需要知道服务名称就好，继续地址都会通过注册中心同步过来。</p>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>WEMIRR-PLATFORM 默认采用的是 <code>Nacos</code> 作为服务发现与配置中心的，如果对于企业已经有 <code>Eureka</code>、<code>ZooKeeper</code>、<code>Consul</code>、<code>Apollo</code>、<code>Spring Cloud Config</code> 的小伙伴们
请自行更换 <code>pom.xml</code> 中的依赖即可（但作者还是比较推荐<code>Nacos</code>）更符合国人</p>
</div>
<p><a href="https://nacos.io/zh-cn/docs/v2/what-is-nacos.html" target="_blank" rel="noreferrer">Nacos  官方文档</a></p>
<h2 id="安装方式" tabindex="-1">安装方式 <a class="header-anchor" href="#安装方式" aria-label="Permalink to &quot;安装方式&quot;">&ZeroWidthSpace;</a></h2>
<p><em>下面提供了三种安装方式，总有一款适合你，如果不适合请联系作者添加文档 Thanks~~~</em></p>
<h3 id="源码安装" tabindex="-1">源码安装 <a class="header-anchor" href="#源码安装" aria-label="Permalink to &quot;源码安装&quot;">&ZeroWidthSpace;</a></h3>
<p>喜欢尝鲜的小伙伴们可以从 <code>Github</code> 上下载源码方式来安装最新版本的 <code>Nacos</code></p>
<div class="language-lua vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">git clone </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">https</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">//</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">github.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">com</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">alibaba</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cd nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">mvn </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Prelease</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Dmaven.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">skip</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> clean install </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">U</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ls </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">al distribution</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">target</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">//</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> change the $version to your actual path</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cd distribution</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">target</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$version</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">bin</span></span></code></pre>
</div><h3 id="压缩包安装" tabindex="-1">压缩包安装 <a class="header-anchor" href="#压缩包安装" aria-label="Permalink to &quot;压缩包安装&quot;">&ZeroWidthSpace;</a></h3>
<p>您可以从 <a href="https://github.com/alibaba/nacos/releases" target="_blank" rel="noreferrer">最新稳定版本</a> 下载 <code>nacos-server-$version.zip</code> 包。</p>
<div class="language-lua vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">unzip nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$version.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">zip</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> 或者 tar </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">xvf nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$version.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">gz</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cd nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">bin</span></span></code></pre>
</div><h3 id="docker-安装" tabindex="-1">Docker 安装 <a class="header-anchor" href="#docker-安装" aria-label="Permalink to &quot;Docker 安装&quot;">&ZeroWidthSpace;</a></h3>
<p>开发测试环境更推荐使用 <code>Docker</code> 的方式安装,简单粗暴易上手（前提是小伙伴们已经安装过 <code>Docker</code>）</p>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p>安装 Nacos （&gt;=2.0）</p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 拉去镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 以 standalone 方式运行镜像 （如果是虚拟机、Unix、Linux 之类的服务器需要暴露 8848、9848、18848、19848）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart=always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span></code></pre>
</div><h2 id="启动方式" tabindex="-1">启动方式 <a class="header-anchor" href="#启动方式" aria-label="Permalink to &quot;启动方式&quot;">&ZeroWidthSpace;</a></h2>
<p>启动命令(<code>standalone</code>代表着单机模式运行，非集群模式)</p>
<h3 id="docker-启动" tabindex="-1">Docker 启动 <a class="header-anchor" href="#docker-启动" aria-label="Permalink to &quot;Docker 启动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><h3 id="linux-unix-mac-启动" tabindex="-1">Linux/Unix/Mac 启动 <a class="header-anchor" href="#linux-unix-mac-启动" aria-label="Permalink to &quot;Linux/Unix/Mac 启动&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>启动命令(<code>standalone</code>代表着单机模式运行，非集群模式)</strong></p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sh</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> startup.sh</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span></code></pre>
</div><p>如果您使用的是<code>ubuntu</code>系统，或者运行脚本报错提示[[符号找不到，可尝试如下运行</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bash</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> startup.sh</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span></code></pre>
</div><h3 id="windows-启动" tabindex="-1">Windows 启动 <a class="header-anchor" href="#windows-启动" aria-label="Permalink to &quot;Windows 启动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">startup.cmd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span></code></pre>
</div><h2 id="如何使用" tabindex="-1">如何使用 <a class="header-anchor" href="#如何使用" aria-label="Permalink to &quot;如何使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="添加依赖" tabindex="-1">添加依赖 <a class="header-anchor" href="#添加依赖" aria-label="Permalink to &quot;添加依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- Spring Cloud Alibaba Nacos 服务发现 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.alibaba.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-alibaba-nacos-discovery&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- Spring Cloud Alibaba Nacos 服务配置 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.alibaba.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-alibaba-nacos-config&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- Spring Boot Web --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.boot&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-boot-starter-web&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 应用名称</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 如果提前在 Ncoas 配置了 可以通过下面的方式导入 nacos 配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">optional:nacos:${spring.application.name}.properties</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 这是完写法 </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">optional:nacos:mybatis-plus-default.yaml?group=DEFAULT_GROUP&#x26;refreshEnabled=true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1:8848</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1:8848</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        refresh-enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><p>在<code>Application</code>启动类加入注解 <code>@EnableDiscoveryClient</code> 即可轻松搞定服务注册于发现</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EnableDiscoveryClient</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SpringBootApplication</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IamApplication</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SneakyThrows</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        SpringApplication.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IamApplication.class, args);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="启动服务" tabindex="-1">启动服务 <a class="header-anchor" href="#启动服务" aria-label="Permalink to &quot;启动服务&quot;">&ZeroWidthSpace;</a></h3>
<p>启动应用程序后，如未发现异常日志，查看<code>Nacos</code>控制台的服务列表</p>
<p><img src="/env/nacos-server-list.png" alt="Nacos服务列表"></p>
]]></description>
            <content:encoded><![CDATA[<h1 id="服务发现" tabindex="-1">服务发现 <a class="header-anchor" href="#服务发现" aria-label="Permalink to &quot;服务发现&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h2>
<blockquote>
<p><strong><code>什么是注册中心</code></strong></p>
</blockquote>
<p>注册中心在微服务项目中扮演着非常重要的角色，是微服务架构中的纽带，类似于通讯录，它记录了服务和服务地址的映射关系。在分布式架构中，服务会注册到这里，当服务需要调用其它服务时，就到这里找到服务的地址，进行调用。</p>
<ul>
<li><strong><code>为什么要使用注册中心</code></strong></li>
</ul>
<p>解决了服务发现的问题。在没有注册中心时候，服务间调用需要知道被调方的地址或者代理地址。当服务更换部署地址，就不得不修改调用当中指定的地址或者修改代理配置。而有了注册中心之后，每个服务在调用别人的时候只需要知道服务名称就好，继续地址都会通过注册中心同步过来。</p>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>WEMIRR-PLATFORM 默认采用的是 <code>Nacos</code> 作为服务发现与配置中心的，如果对于企业已经有 <code>Eureka</code>、<code>ZooKeeper</code>、<code>Consul</code>、<code>Apollo</code>、<code>Spring Cloud Config</code> 的小伙伴们
请自行更换 <code>pom.xml</code> 中的依赖即可（但作者还是比较推荐<code>Nacos</code>）更符合国人</p>
</div>
<p><a href="https://nacos.io/zh-cn/docs/v2/what-is-nacos.html" target="_blank" rel="noreferrer">Nacos  官方文档</a></p>
<h2 id="安装方式" tabindex="-1">安装方式 <a class="header-anchor" href="#安装方式" aria-label="Permalink to &quot;安装方式&quot;">&ZeroWidthSpace;</a></h2>
<p><em>下面提供了三种安装方式，总有一款适合你，如果不适合请联系作者添加文档 Thanks~~~</em></p>
<h3 id="源码安装" tabindex="-1">源码安装 <a class="header-anchor" href="#源码安装" aria-label="Permalink to &quot;源码安装&quot;">&ZeroWidthSpace;</a></h3>
<p>喜欢尝鲜的小伙伴们可以从 <code>Github</code> 上下载源码方式来安装最新版本的 <code>Nacos</code></p>
<div class="language-lua vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">git clone </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">https</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">//</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">github.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">com</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">alibaba</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cd nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">mvn </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Prelease</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Dmaven.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">test</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">skip</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> clean install </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">U</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ls </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">al distribution</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">target</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">//</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> change the $version to your actual path</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cd distribution</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">target</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$version</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">bin</span></span></code></pre>
</div><h3 id="压缩包安装" tabindex="-1">压缩包安装 <a class="header-anchor" href="#压缩包安装" aria-label="Permalink to &quot;压缩包安装&quot;">&ZeroWidthSpace;</a></h3>
<p>您可以从 <a href="https://github.com/alibaba/nacos/releases" target="_blank" rel="noreferrer">最新稳定版本</a> 下载 <code>nacos-server-$version.zip</code> 包。</p>
<div class="language-lua vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">lua</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">unzip nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$version.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">zip</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> 或者 tar </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">xvf nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$version.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">gz</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">cd nacos</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">bin</span></span></code></pre>
</div><h3 id="docker-安装" tabindex="-1">Docker 安装 <a class="header-anchor" href="#docker-安装" aria-label="Permalink to &quot;Docker 安装&quot;">&ZeroWidthSpace;</a></h3>
<p>开发测试环境更推荐使用 <code>Docker</code> 的方式安装,简单粗暴易上手（前提是小伙伴们已经安装过 <code>Docker</code>）</p>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p>安装 Nacos （&gt;=2.0）</p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 拉去镜像</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 以 standalone 方式运行镜像 （如果是虚拟机、Unix、Linux 之类的服务器需要暴露 8848、9848、18848、19848）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart=always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span></code></pre>
</div><h2 id="启动方式" tabindex="-1">启动方式 <a class="header-anchor" href="#启动方式" aria-label="Permalink to &quot;启动方式&quot;">&ZeroWidthSpace;</a></h2>
<p>启动命令(<code>standalone</code>代表着单机模式运行，非集群模式)</p>
<h3 id="docker-启动" tabindex="-1">Docker 启动 <a class="header-anchor" href="#docker-启动" aria-label="Permalink to &quot;Docker 启动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><h3 id="linux-unix-mac-启动" tabindex="-1">Linux/Unix/Mac 启动 <a class="header-anchor" href="#linux-unix-mac-启动" aria-label="Permalink to &quot;Linux/Unix/Mac 启动&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>启动命令(<code>standalone</code>代表着单机模式运行，非集群模式)</strong></p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sh</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> startup.sh</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span></code></pre>
</div><p>如果您使用的是<code>ubuntu</code>系统，或者运行脚本报错提示[[符号找不到，可尝试如下运行</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bash</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> startup.sh</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span></code></pre>
</div><h3 id="windows-启动" tabindex="-1">Windows 启动 <a class="header-anchor" href="#windows-启动" aria-label="Permalink to &quot;Windows 启动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">startup.cmd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -m</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span></code></pre>
</div><h2 id="如何使用" tabindex="-1">如何使用 <a class="header-anchor" href="#如何使用" aria-label="Permalink to &quot;如何使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="添加依赖" tabindex="-1">添加依赖 <a class="header-anchor" href="#添加依赖" aria-label="Permalink to &quot;添加依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- Spring Cloud Alibaba Nacos 服务发现 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.alibaba.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-alibaba-nacos-discovery&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- Spring Cloud Alibaba Nacos 服务配置 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.alibaba.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-alibaba-nacos-config&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- Spring Boot Web --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.boot&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-boot-starter-web&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 应用名称</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 如果提前在 Ncoas 配置了 可以通过下面的方式导入 nacos 配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">optional:nacos:${spring.application.name}.properties</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 这是完写法 </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">optional:nacos:mybatis-plus-default.yaml?group=DEFAULT_GROUP&#x26;refreshEnabled=true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    nacos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1:8848</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-addr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1:8848</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        refresh-enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><p>在<code>Application</code>启动类加入注解 <code>@EnableDiscoveryClient</code> 即可轻松搞定服务注册于发现</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">EnableDiscoveryClient</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SpringBootApplication</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IamApplication</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SneakyThrows</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        SpringApplication.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IamApplication.class, args);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="启动服务" tabindex="-1">启动服务 <a class="header-anchor" href="#启动服务" aria-label="Permalink to &quot;启动服务&quot;">&ZeroWidthSpace;</a></h3>
<p>启动应用程序后，如未发现异常日志，查看<code>Nacos</code>控制台的服务列表</p>
<p><img src="/env/nacos-server-list.png" alt="Nacos服务列表"></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[容器引擎 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/middleware/docker.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/middleware/docker.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="容器引擎" tabindex="-1">容器引擎 <a class="header-anchor" href="#容器引擎" aria-label="Permalink to &quot;容器引擎&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">公告</p>
<p>作者开发测试均采用的 <code>Docker</code> 方式安装的环境,所以文档中很多中间件的安装步骤都是基于 <code>Docker</code> 写的,缺少的部分欢迎大家补充</p>
</div>
<h2 id="安装-docker" tabindex="-1">安装 Docker <a class="header-anchor" href="#安装-docker" aria-label="Permalink to &quot;安装 Docker&quot;">&ZeroWidthSpace;</a></h2>
<p><a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noreferrer">Docker 官方网址</a></p>
<h3 id="下载安装" tabindex="-1">下载安装 <a class="header-anchor" href="#下载安装" aria-label="Permalink to &quot;下载安装&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>下载符合您机器的安装包安装即可,如果是 <code>Unix</code>、<code>Linux</code> 不要可视化操作界面的话，也可以通过 <code>yum</code>、<code>apt-get</code>、<code>brew</code>等方式安装</strong></p>
<p><img src="/env/docker-home.png" alt="Docker 下载安装"></p>
<h3 id="测试" tabindex="-1">测试 <a class="header-anchor" href="#测试" aria-label="Permalink to &quot;测试&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看安装的版本</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span></span></code></pre>
</div><h2 id="扩展内容" tabindex="-1">扩展内容 <a class="header-anchor" href="#扩展内容" aria-label="Permalink to &quot;扩展内容&quot;">&ZeroWidthSpace;</a></h2>
<p>顺便推荐安装 <code>docker-compose</code> 方便快速搭建开发环境</p>
]]></description>
            <content:encoded><![CDATA[<h1 id="容器引擎" tabindex="-1">容器引擎 <a class="header-anchor" href="#容器引擎" aria-label="Permalink to &quot;容器引擎&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">公告</p>
<p>作者开发测试均采用的 <code>Docker</code> 方式安装的环境,所以文档中很多中间件的安装步骤都是基于 <code>Docker</code> 写的,缺少的部分欢迎大家补充</p>
</div>
<h2 id="安装-docker" tabindex="-1">安装 Docker <a class="header-anchor" href="#安装-docker" aria-label="Permalink to &quot;安装 Docker&quot;">&ZeroWidthSpace;</a></h2>
<p><a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noreferrer">Docker 官方网址</a></p>
<h3 id="下载安装" tabindex="-1">下载安装 <a class="header-anchor" href="#下载安装" aria-label="Permalink to &quot;下载安装&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>下载符合您机器的安装包安装即可,如果是 <code>Unix</code>、<code>Linux</code> 不要可视化操作界面的话，也可以通过 <code>yum</code>、<code>apt-get</code>、<code>brew</code>等方式安装</strong></p>
<p><img src="/env/docker-home.png" alt="Docker 下载安装"></p>
<h3 id="测试" tabindex="-1">测试 <a class="header-anchor" href="#测试" aria-label="Permalink to &quot;测试&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 查看安装的版本</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span></span></code></pre>
</div><h2 id="扩展内容" tabindex="-1">扩展内容 <a class="header-anchor" href="#扩展内容" aria-label="Permalink to &quot;扩展内容&quot;">&ZeroWidthSpace;</a></h2>
<p>顺便推荐安装 <code>docker-compose</code> 方便快速搭建开发环境</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[异常处理 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/middleware/exception.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/middleware/exception.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="异常处理" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h1>
<p>通常一个<code>web</code>框架中，有大量需要处理的异常。比如业务异常，权限不足等等。前端通过弹出提示信息的方式告诉用户出了什么错误。
通常情况下我们用<code>try.....catch....</code>对异常进行捕捉处理，但是在实际项目中对业务模块进行异常捕捉，会造成代码重复和繁杂，
我们希望代码中只有业务相关的操作，所有的异常我们单独设立一个类来处理它。全局异常就是对框架所有异常进行统一管理。
我们在可能发生异常的方法里<code>throw</code>抛给控制器。然后由全局异常处理器对异常进行统一处理。 如此，我们的<code>Controller</code>中的方法就可以很简洁了。</p>
<p>所谓全局异常处理器就是使用@ControllerAdvice注解。示例如下：</p>
<ul>
<li>在 <code>GlobalExceptionHandler</code> 类中添加需要同意处理的异常方法</li>
</ul>
<p>例如：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    * @ExceptionHandler 指定需要捕获的异常</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    **/</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExceptionHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataIntegrityViolationException.class)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseBody</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ResponseEntity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dataIntegrityViolationException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataIntegrityViolationException e, HttpServletRequest request) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">[================================================================]</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">                "[异常信息] - [{}]</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">                "[================================================================]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getLocalizedMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCause</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SQLException) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCause</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="异常处理" tabindex="-1">异常处理 <a class="header-anchor" href="#异常处理" aria-label="Permalink to &quot;异常处理&quot;">&ZeroWidthSpace;</a></h1>
<p>通常一个<code>web</code>框架中，有大量需要处理的异常。比如业务异常，权限不足等等。前端通过弹出提示信息的方式告诉用户出了什么错误。
通常情况下我们用<code>try.....catch....</code>对异常进行捕捉处理，但是在实际项目中对业务模块进行异常捕捉，会造成代码重复和繁杂，
我们希望代码中只有业务相关的操作，所有的异常我们单独设立一个类来处理它。全局异常就是对框架所有异常进行统一管理。
我们在可能发生异常的方法里<code>throw</code>抛给控制器。然后由全局异常处理器对异常进行统一处理。 如此，我们的<code>Controller</code>中的方法就可以很简洁了。</p>
<p>所谓全局异常处理器就是使用@ControllerAdvice注解。示例如下：</p>
<ul>
<li>在 <code>GlobalExceptionHandler</code> 类中添加需要同意处理的异常方法</li>
</ul>
<p>例如：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    * @ExceptionHandler 指定需要捕获的异常</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    **/</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExceptionHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataIntegrityViolationException.class)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseBody</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ResponseEntity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dataIntegrityViolationException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataIntegrityViolationException e, HttpServletRequest request) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">[================================================================]</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">                "[异常信息] - [{}]</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">                "[================================================================]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getLocalizedMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCause</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SQLException) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCause</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(e.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[缓存存储 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/middleware/redis.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/middleware/redis.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="缓存存储" tabindex="-1">缓存存储 <a class="header-anchor" href="#缓存存储" aria-label="Permalink to &quot;缓存存储&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p><code>WEMIRR-PLATFORM</code> 默认缓存是 <code>Redis</code>，如果有变动请自行替换即可</p>
</div>
<h2 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h2>
<p><a href="https://redis.io/docs/getting-started/" target="_blank" rel="noreferrer">Redis  官方文档</a></p>
<h2 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="docker" tabindex="-1">Docker <a class="header-anchor" href="#docker" aria-label="Permalink to &quot;Docker&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p>安装 Redis （&gt;=3.0）</p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 版本能新就新</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span></span></code></pre>
</div><h3 id="常规安装" tabindex="-1">常规安装 <a class="header-anchor" href="#常规安装" aria-label="Permalink to &quot;常规安装&quot;">&ZeroWidthSpace;</a></h3>
<p>请参考 <a href="https://redis.io/docs/getting-started/" target="_blank" rel="noreferrer">Redis  官方文档</a> 进行安装即可</p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-from-source" target="_blank" rel="noreferrer">源码安装</a> 这种方式需要自己编辑比较麻烦</p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-on-linux" target="_blank" rel="noreferrer">Linux OS 安装方式</a></p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-on-mac-os" target="_blank" rel="noreferrer">Mac OS 安装方式</a></p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-on-windows" target="_blank" rel="noreferrer">Windows Os 安装方式</a></p>
]]></description>
            <content:encoded><![CDATA[<h1 id="缓存存储" tabindex="-1">缓存存储 <a class="header-anchor" href="#缓存存储" aria-label="Permalink to &quot;缓存存储&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p><code>WEMIRR-PLATFORM</code> 默认缓存是 <code>Redis</code>，如果有变动请自行替换即可</p>
</div>
<h2 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h2>
<p><a href="https://redis.io/docs/getting-started/" target="_blank" rel="noreferrer">Redis  官方文档</a></p>
<h2 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="docker" tabindex="-1">Docker <a class="header-anchor" href="#docker" aria-label="Permalink to &quot;Docker&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p>安装 Redis （&gt;=3.0）</p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 版本能新就新</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -itd</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span></span></code></pre>
</div><h3 id="常规安装" tabindex="-1">常规安装 <a class="header-anchor" href="#常规安装" aria-label="Permalink to &quot;常规安装&quot;">&ZeroWidthSpace;</a></h3>
<p>请参考 <a href="https://redis.io/docs/getting-started/" target="_blank" rel="noreferrer">Redis  官方文档</a> 进行安装即可</p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-from-source" target="_blank" rel="noreferrer">源码安装</a> 这种方式需要自己编辑比较麻烦</p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-on-linux" target="_blank" rel="noreferrer">Linux OS 安装方式</a></p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-on-mac-os" target="_blank" rel="noreferrer">Mac OS 安装方式</a></p>
<p><a href="https://redis.io/docs/getting-started/installation/install-redis-on-windows" target="_blank" rel="noreferrer">Windows Os 安装方式</a></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[链路追踪 ]]></title>
            <link>https://docs.battcn.com/zh/guide/env/middleware/skywalking.html</link>
            <guid>https://docs.battcn.com/zh/guide/env/middleware/skywalking.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="链路追踪" tabindex="-1">链路追踪 <a class="header-anchor" href="#链路追踪" aria-label="Permalink to &quot;链路追踪&quot;">&ZeroWidthSpace;</a></h1>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p>非必选,一般公司用不上,太吃服务器资源了</p>
<p>skywalking-es7（<strong>8.7.0 有问题,别安装</strong>）</p>
</div>
<h2 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="docker" tabindex="-1">Docker <a class="header-anchor" href="#docker" aria-label="Permalink to &quot;Docker&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果已执行请忽略</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9200:9200</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9300:9300</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "discovery.type=single-node"</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 1234:1234</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 12800:12800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 11800:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE=elasticsearch7</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap-ui</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10086:8080</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TZ=Asia/Shanghai</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_OAP_ADDRESS=oap:12800</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span></code></pre>
</div><h2 id="使用" tabindex="-1">使用 <a class="header-anchor" href="#使用" aria-label="Permalink to &quot;使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="idea启用" tabindex="-1">IDEA启用 <a class="header-anchor" href="#idea启用" aria-label="Permalink to &quot;IDEA启用&quot;">&ZeroWidthSpace;</a></h3>
<p>通过IDEA提供的参数配置功能，配置我们的 <code>skywalking</code> 监控插件</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">VmOption</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/Users/battcn/Desktop/apache-skywalking-apm-bin/agent/skywalking-agent.jar</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-iam</span></span></code></pre>
</div><h3 id="命令启用" tabindex="-1">命令启用 <a class="header-anchor" href="#命令启用" aria-label="Permalink to &quot;命令启用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-gateway</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-gateway.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_gateway.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-iam</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-iam.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --spring.profiles.active=demo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_iam.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="链路追踪" tabindex="-1">链路追踪 <a class="header-anchor" href="#链路追踪" aria-label="Permalink to &quot;链路追踪&quot;">&ZeroWidthSpace;</a></h1>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p>非必选,一般公司用不上,太吃服务器资源了</p>
<p>skywalking-es7（<strong>8.7.0 有问题,别安装</strong>）</p>
</div>
<h2 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="docker" tabindex="-1">Docker <a class="header-anchor" href="#docker" aria-label="Permalink to &quot;Docker&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果已执行请忽略</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pull</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9200:9200</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9300:9300</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "discovery.type=single-node"</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 1234:1234</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 12800:12800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 11800:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE=elasticsearch7</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> oap-ui</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --restart</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10086:8080</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TZ=Asia/Shanghai</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_OAP_ADDRESS=oap:12800</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span></code></pre>
</div><h2 id="使用" tabindex="-1">使用 <a class="header-anchor" href="#使用" aria-label="Permalink to &quot;使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="idea启用" tabindex="-1">IDEA启用 <a class="header-anchor" href="#idea启用" aria-label="Permalink to &quot;IDEA启用&quot;">&ZeroWidthSpace;</a></h3>
<p>通过IDEA提供的参数配置功能，配置我们的 <code>skywalking</code> 监控插件</p>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">VmOption</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/Users/battcn/Desktop/apache-skywalking-apm-bin/agent/skywalking-agent.jar</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Environment</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> variables</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> SW_AGENT_NAME=wemirr-platform-iam</span></span></code></pre>
</div><h3 id="命令启用" tabindex="-1">命令启用 <a class="header-anchor" href="#命令启用" aria-label="Permalink to &quot;命令启用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-gateway</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-gateway.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_gateway.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nohup</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -javaagent:/opt/wemirr-platform/skywalking/agent/skywalking-agent.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.agent.service_name=wemirr-platform-iam</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -Dskywalking.collector.backend_service=127.0.0.1:11800</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -jar</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-iam.jar</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --spring.profiles.active=demo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> logs/start_iam.log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[组件使用指南 ]]></title>
            <link>https://docs.battcn.com/zh/guide/frontend/components.html</link>
            <guid>https://docs.battcn.com/zh/guide/frontend/components.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="组件使用指南" tabindex="-1">组件使用指南 <a class="header-anchor" href="#组件使用指南" aria-label="Permalink to &quot;组件使用指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 内置组件的使用方法</p>
</div>
<h2 id="组件概览" tabindex="-1">组件概览 <a class="header-anchor" href="#组件概览" aria-label="Permalink to &quot;组件概览&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 基于 Vben Admin 5.x 和 Ant Design Vue 4.x，提供了丰富的业务组件。</p>
<h3 id="基础组件" tabindex="-1">基础组件 <a class="header-anchor" href="#基础组件" aria-label="Permalink to &quot;基础组件&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BasicForm</code></td>
<td>表单组件，支持动态表单</td>
</tr>
<tr>
<td><code>BasicTable</code></td>
<td>表格组件，支持分页、筛选</td>
</tr>
<tr>
<td><code>BasicModal</code></td>
<td>弹窗组件</td>
</tr>
<tr>
<td><code>BasicDrawer</code></td>
<td>抽屉组件</td>
</tr>
</tbody>
</table>
<h3 id="业务组件" tabindex="-1">业务组件 <a class="header-anchor" href="#业务组件" aria-label="Permalink to &quot;业务组件&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>DictSelect</code></td>
<td>字典下拉选择</td>
</tr>
<tr>
<td><code>TreeSelect</code></td>
<td>树形选择器</td>
</tr>
<tr>
<td><code>Upload</code></td>
<td>文件上传</td>
</tr>
<tr>
<td><code>Editor</code></td>
<td>富文本编辑器</td>
</tr>
</tbody>
</table>
<h2 id="basicform-表单组件" tabindex="-1">BasicForm 表单组件 <a class="header-anchor" href="#basicform-表单组件" aria-label="Permalink to &quot;BasicForm 表单组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicForm, useForm } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { FormSchema } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> schemas</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FormSchema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    colProps: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    rules: [{ type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入有效的邮箱地址'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    colProps: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    componentProps: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      options: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'remark'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'备注'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'InputTextArea'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    colProps: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">24</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">validate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">getFieldsValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">setFieldsValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resetFields</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  schemas,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  labelWidth: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  showActionButtonGroup: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  actionColOptions: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">24</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  submitButtonOptions: { text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'提交'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  resetButtonOptions: { text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'重置'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleSubmit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> values</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> validate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'表单数据:'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, values);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerForm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">submit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleSubmit</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="表单配置项" tabindex="-1">表单配置项 <a class="header-anchor" href="#表单配置项" aria-label="Permalink to &quot;表单配置项&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FormSchema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  field</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段名</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  label</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 标签</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  component</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件类型</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  componentProps</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件属性</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  required</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否必填</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  rules</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 校验规则</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  colProps</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 栅格配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  slot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义插槽</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ifShow</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Fn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  dynamicDisabled</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Fn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否禁用</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  defaultValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 默认值</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="支持的组件类型" tabindex="-1">支持的组件类型 <a class="header-anchor" href="#支持的组件类型" aria-label="Permalink to &quot;支持的组件类型&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Input</code></td>
<td>输入框</td>
</tr>
<tr>
<td><code>InputNumber</code></td>
<td>数字输入框</td>
</tr>
<tr>
<td><code>InputPassword</code></td>
<td>密码输入框</td>
</tr>
<tr>
<td><code>InputTextArea</code></td>
<td>文本域</td>
</tr>
<tr>
<td><code>Select</code></td>
<td>下拉选择</td>
</tr>
<tr>
<td><code>TreeSelect</code></td>
<td>树形选择</td>
</tr>
<tr>
<td><code>RadioGroup</code></td>
<td>单选组</td>
</tr>
<tr>
<td><code>CheckboxGroup</code></td>
<td>多选组</td>
</tr>
<tr>
<td><code>Switch</code></td>
<td>开关</td>
</tr>
<tr>
<td><code>DatePicker</code></td>
<td>日期选择</td>
</tr>
<tr>
<td><code>RangePicker</code></td>
<td>日期范围</td>
</tr>
<tr>
<td><code>TimePicker</code></td>
<td>时间选择</td>
</tr>
<tr>
<td><code>Upload</code></td>
<td>上传</td>
</tr>
</tbody>
</table>
<h3 id="表单联动" tabindex="-1">表单联动 <a class="header-anchor" href="#表单联动" aria-label="Permalink to &quot;表单联动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> schemas</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FormSchema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'type'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'类型'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    componentProps: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      options: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'个人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'企业'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'companyName'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'公司名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 仅当类型为企业时显示</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ifShow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> values.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'idCard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'身份证号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 仅当类型为个人时显示</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ifShow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> values.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span></code></pre>
</div><h2 id="basictable-表格组件" tabindex="-1">BasicTable 表格组件 <a class="header-anchor" href="#basictable-表格组件" aria-label="Permalink to &quot;BasicTable 表格组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-1" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-1" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicTable, useTable } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicColumn } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, deleteUser } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BasicColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">120</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    customRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> record.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '启用'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'createTime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">reload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">getSelectRows</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  columns,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  api: getUserList,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowKey: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  bordered: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  showIndexColumn: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowSelection: { type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'checkbox'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  pagination: { pageSize: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  actionColumn: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'操作'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">150</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    fixed: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'right'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleEdit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'编辑:'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, record);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleDelete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(record.id);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  reload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerTable</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> #</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toolbar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"primary"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleAdd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>新增&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleBatchDelete</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>批量删除&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> #</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">action</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{ record }</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"small"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleEdit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(record)</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        编辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-popconfirm</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"确定删除？"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">confirm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleDelete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(record)</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"small"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> danger</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>删除&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-popconfirm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="表格配置项" tabindex="-1">表格配置项 <a class="header-anchor" href="#表格配置项" aria-label="Permalink to &quot;表格配置项&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TableProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BasicColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  api</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据接口</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  dataSource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];         </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 静态数据</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  rowKey</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 行键</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  bordered</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;         </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示边框</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  pagination</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分页配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  rowSelection</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 行选择配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  showIndexColumn</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示序号列</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  actionColumn</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 操作列配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  searchInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 额外搜索参数</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  beforeFetch</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 请求前处理</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  afterFetch</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 请求后处理</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="表格方法" tabindex="-1">表格方法 <a class="header-anchor" href="#表格方法" aria-label="Permalink to &quot;表格方法&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>reload(opt?)</code></td>
<td>刷新表格数据</td>
</tr>
<tr>
<td><code>getSelectRows()</code></td>
<td>获取选中行数据</td>
</tr>
<tr>
<td><code>getSelectRowKeys()</code></td>
<td>获取选中行键</td>
</tr>
<tr>
<td><code>clearSelectedRowKeys()</code></td>
<td>清空选中</td>
</tr>
<tr>
<td><code>setSelectedRowKeys(keys)</code></td>
<td>设置选中行</td>
</tr>
<tr>
<td><code>getPaginationRef()</code></td>
<td>获取分页信息</td>
</tr>
<tr>
<td><code>setPagination(info)</code></td>
<td>设置分页</td>
</tr>
<tr>
<td><code>getDataSource()</code></td>
<td>获取表格数据</td>
</tr>
<tr>
<td><code>setTableData(data)</code></td>
<td>设置表格数据</td>
</tr>
</tbody>
</table>
<h2 id="basicmodal-弹窗组件" tabindex="-1">BasicModal 弹窗组件 <a class="header-anchor" href="#basicmodal-弹窗组件" aria-label="Permalink to &quot;BasicModal 弹窗组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-2" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-2" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicModal, useModal } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">openModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">closeModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">setModalProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleOpen</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  openModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { id: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleOpen</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>打开弹窗&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicModal</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerModal</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户详情"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">width</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">600</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ok</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleSubmit</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>弹窗内容&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="弹窗方法" tabindex="-1">弹窗方法 <a class="header-anchor" href="#弹窗方法" aria-label="Permalink to &quot;弹窗方法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  openModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 打开弹窗，可传递数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  closeModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 关闭弹窗</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  setModalProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 设置弹窗属性</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  getVisible</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 获取显示状态</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 在子组件中接收数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">closeModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useModalInner</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'接收到的数据:'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h2 id="basicdrawer-抽屉组件" tabindex="-1">BasicDrawer 抽屉组件 <a class="header-anchor" href="#basicdrawer-抽屉组件" aria-label="Permalink to &quot;BasicDrawer 抽屉组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-3" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-3" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicDrawer, useDrawer } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">openDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">openDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { id: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> })</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    打开抽屉</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicDrawer</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerDrawer</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"详情"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">width</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">500</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>抽屉内容&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="字典组件" tabindex="-1">字典组件 <a class="header-anchor" href="#字典组件" aria-label="Permalink to &quot;字典组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="dictselect-字典下拉" tabindex="-1">DictSelect 字典下拉 <a class="header-anchor" href="#dictselect-字典下拉" aria-label="Permalink to &quot;DictSelect 字典下拉&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { DictSelect } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/components/Dict'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 基础用法 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictSelect</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">formData.status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_status"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 多选 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictSelect</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">formData.tags</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_tags"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    mode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"multiple"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 在表单中使用 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-form-item</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> label</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"状态"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictSelect</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">formData.status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_status"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-form-item</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="dicttag-字典标签" tabindex="-1">DictTag 字典标签 <a class="header-anchor" href="#dicttag-字典标签" aria-label="Permalink to &quot;DictTag 字典标签&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 显示字典标签 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictTag</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_status"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">record.status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="文件上传组件" tabindex="-1">文件上传组件 <a class="header-anchor" href="#文件上传组件" aria-label="Permalink to &quot;文件上传组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-4" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-4" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicUpload } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/components/Upload'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> fileList</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([]);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleChange</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  fileList.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 单文件上传 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicUpload</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">fileList</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max-count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">api</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">uploadApi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">change</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleChange</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 多文件上传 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicUpload</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">fileList</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max-count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">api</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">uploadApi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    accept</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">".jpg,.png,.pdf"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 图片上传 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicUpload</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">fileList</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">api</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">uploadApi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    list-type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"picture-card"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    accept</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"image/*"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="树形选择器" tabindex="-1">树形选择器 <a class="header-anchor" href="#树形选择器" aria-label="Permalink to &quot;树形选择器&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="部门树选择" tabindex="-1">部门树选择 <a class="header-anchor" href="#部门树选择" aria-label="Permalink to &quot;部门树选择&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { TreeSelect } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ant-design-vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getDeptTree } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/api/system/dept'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> treeData</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([]);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> selectedDept</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">onMounted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  treeData.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getDeptTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">TreeSelect</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">selectedDept</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tree-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">treeData</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">field-names</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{ label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'name'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, children: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'children'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    placeholder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"请选择部门"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    allow-clear</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    tree-default-expand-all</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="图标选择器" tabindex="-1">图标选择器 <a class="header-anchor" href="#图标选择器" aria-label="Permalink to &quot;图标选择器&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { IconPicker } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/components/Icon'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> selectedIcon</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">IconPicker</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">selectedIcon</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/frontend/crud.html">CRUD 开发</a> - 快速开发增删改查页面</li>
<li><a href="/zh/guide/frontend/permission.html">权限控制</a> - 前端权限控制详解</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="组件使用指南" tabindex="-1">组件使用指南 <a class="header-anchor" href="#组件使用指南" aria-label="Permalink to &quot;组件使用指南&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 内置组件的使用方法</p>
</div>
<h2 id="组件概览" tabindex="-1">组件概览 <a class="header-anchor" href="#组件概览" aria-label="Permalink to &quot;组件概览&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 基于 Vben Admin 5.x 和 Ant Design Vue 4.x，提供了丰富的业务组件。</p>
<h3 id="基础组件" tabindex="-1">基础组件 <a class="header-anchor" href="#基础组件" aria-label="Permalink to &quot;基础组件&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BasicForm</code></td>
<td>表单组件，支持动态表单</td>
</tr>
<tr>
<td><code>BasicTable</code></td>
<td>表格组件，支持分页、筛选</td>
</tr>
<tr>
<td><code>BasicModal</code></td>
<td>弹窗组件</td>
</tr>
<tr>
<td><code>BasicDrawer</code></td>
<td>抽屉组件</td>
</tr>
</tbody>
</table>
<h3 id="业务组件" tabindex="-1">业务组件 <a class="header-anchor" href="#业务组件" aria-label="Permalink to &quot;业务组件&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>DictSelect</code></td>
<td>字典下拉选择</td>
</tr>
<tr>
<td><code>TreeSelect</code></td>
<td>树形选择器</td>
</tr>
<tr>
<td><code>Upload</code></td>
<td>文件上传</td>
</tr>
<tr>
<td><code>Editor</code></td>
<td>富文本编辑器</td>
</tr>
</tbody>
</table>
<h2 id="basicform-表单组件" tabindex="-1">BasicForm 表单组件 <a class="header-anchor" href="#basicform-表单组件" aria-label="Permalink to &quot;BasicForm 表单组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicForm, useForm } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { FormSchema } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> schemas</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FormSchema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    colProps: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    rules: [{ type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入有效的邮箱地址'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    colProps: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    componentProps: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      options: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'remark'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'备注'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'InputTextArea'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    colProps: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">24</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">validate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">getFieldsValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">setFieldsValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resetFields</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  schemas,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  labelWidth: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  showActionButtonGroup: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  actionColOptions: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">24</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  submitButtonOptions: { text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'提交'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  resetButtonOptions: { text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'重置'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleSubmit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> values</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> validate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'表单数据:'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, values);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerForm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">submit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleSubmit</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="表单配置项" tabindex="-1">表单配置项 <a class="header-anchor" href="#表单配置项" aria-label="Permalink to &quot;表单配置项&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FormSchema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  field</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段名</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  label</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 标签</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  component</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件类型</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  componentProps</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件属性</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  required</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否必填</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  rules</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 校验规则</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  colProps</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 栅格配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  slot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义插槽</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ifShow</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Fn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  dynamicDisabled</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Fn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否禁用</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  defaultValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 默认值</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="支持的组件类型" tabindex="-1">支持的组件类型 <a class="header-anchor" href="#支持的组件类型" aria-label="Permalink to &quot;支持的组件类型&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Input</code></td>
<td>输入框</td>
</tr>
<tr>
<td><code>InputNumber</code></td>
<td>数字输入框</td>
</tr>
<tr>
<td><code>InputPassword</code></td>
<td>密码输入框</td>
</tr>
<tr>
<td><code>InputTextArea</code></td>
<td>文本域</td>
</tr>
<tr>
<td><code>Select</code></td>
<td>下拉选择</td>
</tr>
<tr>
<td><code>TreeSelect</code></td>
<td>树形选择</td>
</tr>
<tr>
<td><code>RadioGroup</code></td>
<td>单选组</td>
</tr>
<tr>
<td><code>CheckboxGroup</code></td>
<td>多选组</td>
</tr>
<tr>
<td><code>Switch</code></td>
<td>开关</td>
</tr>
<tr>
<td><code>DatePicker</code></td>
<td>日期选择</td>
</tr>
<tr>
<td><code>RangePicker</code></td>
<td>日期范围</td>
</tr>
<tr>
<td><code>TimePicker</code></td>
<td>时间选择</td>
</tr>
<tr>
<td><code>Upload</code></td>
<td>上传</td>
</tr>
</tbody>
</table>
<h3 id="表单联动" tabindex="-1">表单联动 <a class="header-anchor" href="#表单联动" aria-label="Permalink to &quot;表单联动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> schemas</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FormSchema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'type'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'类型'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    componentProps: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      options: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'个人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'企业'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'companyName'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'公司名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 仅当类型为企业时显示</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ifShow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> values.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'idCard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'身份证号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 仅当类型为个人时显示</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ifShow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> values.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span></code></pre>
</div><h2 id="basictable-表格组件" tabindex="-1">BasicTable 表格组件 <a class="header-anchor" href="#basictable-表格组件" aria-label="Permalink to &quot;BasicTable 表格组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-1" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-1" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicTable, useTable } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicColumn } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, deleteUser } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BasicColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">120</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    customRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> record.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '启用'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'createTime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">reload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">getSelectRows</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  columns,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  api: getUserList,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowKey: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  bordered: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  showIndexColumn: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowSelection: { type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'checkbox'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  pagination: { pageSize: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  actionColumn: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'操作'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">150</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    fixed: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'right'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleEdit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'编辑:'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, record);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleDelete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(record.id);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  reload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerTable</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> #</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toolbar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"primary"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleAdd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>新增&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleBatchDelete</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>批量删除&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> #</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">action</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{ record }</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"small"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleEdit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(record)</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        编辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-popconfirm</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"确定删除？"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">confirm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleDelete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(record)</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"small"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> danger</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>删除&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-popconfirm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicTable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="表格配置项" tabindex="-1">表格配置项 <a class="header-anchor" href="#表格配置项" aria-label="Permalink to &quot;表格配置项&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TableProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BasicColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  api</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据接口</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  dataSource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];         </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 静态数据</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  rowKey</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 行键</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  bordered</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;         </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示边框</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  pagination</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 分页配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  rowSelection</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 行选择配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  showIndexColumn</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示序号列</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  actionColumn</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 操作列配置</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  searchInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 额外搜索参数</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  beforeFetch</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 请求前处理</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  afterFetch</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 请求后处理</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="表格方法" tabindex="-1">表格方法 <a class="header-anchor" href="#表格方法" aria-label="Permalink to &quot;表格方法&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>reload(opt?)</code></td>
<td>刷新表格数据</td>
</tr>
<tr>
<td><code>getSelectRows()</code></td>
<td>获取选中行数据</td>
</tr>
<tr>
<td><code>getSelectRowKeys()</code></td>
<td>获取选中行键</td>
</tr>
<tr>
<td><code>clearSelectedRowKeys()</code></td>
<td>清空选中</td>
</tr>
<tr>
<td><code>setSelectedRowKeys(keys)</code></td>
<td>设置选中行</td>
</tr>
<tr>
<td><code>getPaginationRef()</code></td>
<td>获取分页信息</td>
</tr>
<tr>
<td><code>setPagination(info)</code></td>
<td>设置分页</td>
</tr>
<tr>
<td><code>getDataSource()</code></td>
<td>获取表格数据</td>
</tr>
<tr>
<td><code>setTableData(data)</code></td>
<td>设置表格数据</td>
</tr>
</tbody>
</table>
<h2 id="basicmodal-弹窗组件" tabindex="-1">BasicModal 弹窗组件 <a class="header-anchor" href="#basicmodal-弹窗组件" aria-label="Permalink to &quot;BasicModal 弹窗组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-2" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-2" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicModal, useModal } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">openModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">closeModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">setModalProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleOpen</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  openModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { id: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleOpen</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>打开弹窗&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicModal</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerModal</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户详情"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">width</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">600</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ok</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleSubmit</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>弹窗内容&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="弹窗方法" tabindex="-1">弹窗方法 <a class="header-anchor" href="#弹窗方法" aria-label="Permalink to &quot;弹窗方法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  openModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 打开弹窗，可传递数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  closeModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 关闭弹窗</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  setModalProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 设置弹窗属性</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  getVisible</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 获取显示状态</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 在子组件中接收数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">closeModal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useModalInner</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'接收到的数据:'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h2 id="basicdrawer-抽屉组件" tabindex="-1">BasicDrawer 抽屉组件 <a class="header-anchor" href="#basicdrawer-抽屉组件" aria-label="Permalink to &quot;BasicDrawer 抽屉组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-3" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-3" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicDrawer, useDrawer } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@vben/common-ui'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">registerDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">openDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">openDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { id: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> })</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    打开抽屉</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicDrawer</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registerDrawer</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"详情"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">width</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">500</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>抽屉内容&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicDrawer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="字典组件" tabindex="-1">字典组件 <a class="header-anchor" href="#字典组件" aria-label="Permalink to &quot;字典组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="dictselect-字典下拉" tabindex="-1">DictSelect 字典下拉 <a class="header-anchor" href="#dictselect-字典下拉" aria-label="Permalink to &quot;DictSelect 字典下拉&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { DictSelect } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/components/Dict'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 基础用法 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictSelect</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">formData.status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_status"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 多选 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictSelect</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">formData.tags</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_tags"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    mode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"multiple"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 在表单中使用 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-form-item</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> label</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"状态"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictSelect</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">formData.status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_status"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-form-item</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="dicttag-字典标签" tabindex="-1">DictTag 字典标签 <a class="header-anchor" href="#dicttag-字典标签" aria-label="Permalink to &quot;DictTag 字典标签&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 显示字典标签 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">DictTag</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dict-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys_status"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">record.status</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="文件上传组件" tabindex="-1">文件上传组件 <a class="header-anchor" href="#文件上传组件" aria-label="Permalink to &quot;文件上传组件&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="基础用法-4" tabindex="-1">基础用法 <a class="header-anchor" href="#基础用法-4" aria-label="Permalink to &quot;基础用法&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { BasicUpload } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/components/Upload'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> fileList</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([]);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleChange</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  fileList.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 单文件上传 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicUpload</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">fileList</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max-count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">api</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">uploadApi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">change</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleChange</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 多文件上传 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicUpload</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">fileList</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max-count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">api</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">uploadApi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    accept</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">".jpg,.png,.pdf"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 图片上传 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">BasicUpload</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">fileList</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">api</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">uploadApi</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    list-type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"picture-card"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    accept</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"image/*"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="树形选择器" tabindex="-1">树形选择器 <a class="header-anchor" href="#树形选择器" aria-label="Permalink to &quot;树形选择器&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="部门树选择" tabindex="-1">部门树选择 <a class="header-anchor" href="#部门树选择" aria-label="Permalink to &quot;部门树选择&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { TreeSelect } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ant-design-vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getDeptTree } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/api/system/dept'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> treeData</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([]);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> selectedDept</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">onMounted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  treeData.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getDeptTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">TreeSelect</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">selectedDept</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tree-data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">treeData</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">field-names</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{ label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'name'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, children: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'children'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    placeholder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"请选择部门"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    allow-clear</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    tree-default-expand-all</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="图标选择器" tabindex="-1">图标选择器 <a class="header-anchor" href="#图标选择器" aria-label="Permalink to &quot;图标选择器&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { IconPicker } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/components/Icon'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> selectedIcon</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">IconPicker</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-model</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">selectedIcon</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/frontend/crud.html">CRUD 开发</a> - 快速开发增删改查页面</li>
<li><a href="/zh/guide/frontend/permission.html">权限控制</a> - 前端权限控制详解</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CRUD 快速开发 ]]></title>
            <link>https://docs.battcn.com/zh/guide/frontend/crud.html</link>
            <guid>https://docs.battcn.com/zh/guide/frontend/crud.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="crud-快速开发" tabindex="-1">CRUD 快速开发 <a class="header-anchor" href="#crud-快速开发" aria-label="Permalink to &quot;CRUD 快速开发&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握使用 Fast Crud 快速开发增删改查页面</p>
</div>
<h2 id="简介" tabindex="-1">简介 <a class="header-anchor" href="#简介" aria-label="Permalink to &quot;简介&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 集成了 <a href="http://fast-crud.docmirror.cn/" target="_blank" rel="noreferrer">Fast Crud</a>，只需几行代码即可完成 CRUD 页面开发。</p>
<h2 id="基础示例" tabindex="-1">基础示例 <a class="header-anchor" href="#基础示例" aria-label="Permalink to &quot;基础示例&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="最简示例" tabindex="-1">最简示例 <a class="header-anchor" href="#最简示例" aria-label="Permalink to &quot;最简示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useFs } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, createUser, updateUser, deleteUser } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useFs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { records: res.data.records, total: res.data.total };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.id, form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      email: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dict: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      createTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">crudBinding</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><p>只需要上面这段代码，就能得到一个完整的 CRUD 页面，包含：</p>
<ul>
<li>✅ 数据列表</li>
<li>✅ 分页</li>
<li>✅ 搜索</li>
<li>✅ 新增</li>
<li>✅ 编辑</li>
<li>✅ 删除</li>
</ul>
<h2 id="配置详解" tabindex="-1">配置详解 <a class="header-anchor" href="#配置详解" aria-label="Permalink to &quot;配置详解&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="columns-列配置" tabindex="-1">columns 列配置 <a class="header-anchor" href="#columns-列配置" aria-label="Permalink to &quot;columns 列配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 字段名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列标题</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段类型</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 列表配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    column: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">120</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列宽</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      fixed: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'left'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 固定列</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      sortable: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否可排序</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      align: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'center'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 对齐方式</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      formatter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">context</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 格式化</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 表单配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      disabled: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否禁用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      rules: [],                  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 校验规则</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: {},              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      col: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 栅格配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 搜索配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    search: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: {},              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 新增表单配置（覆盖 form）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    addForm: {},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 编辑表单配置（覆盖 form）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    editForm: {},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查看表单配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    viewForm: {},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="字段类型" tabindex="-1">字段类型 <a class="header-anchor" href="#字段类型" aria-label="Permalink to &quot;字段类型&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>text</code></td>
<td>文本输入</td>
<td>用户名、标题</td>
</tr>
<tr>
<td><code>textarea</code></td>
<td>多行文本</td>
<td>备注、描述</td>
</tr>
<tr>
<td><code>number</code></td>
<td>数字输入</td>
<td>年龄、数量</td>
</tr>
<tr>
<td><code>password</code></td>
<td>密码输入</td>
<td>密码</td>
</tr>
<tr>
<td><code>select</code></td>
<td>下拉选择</td>
<td>状态</td>
</tr>
<tr>
<td><code>dict-select</code></td>
<td>字典下拉</td>
<td>带字典的选择</td>
</tr>
<tr>
<td><code>radio</code></td>
<td>单选</td>
<td>性别</td>
</tr>
<tr>
<td><code>checkbox</code></td>
<td>多选</td>
<td>爱好</td>
</tr>
<tr>
<td><code>switch</code></td>
<td>开关</td>
<td>是否启用</td>
</tr>
<tr>
<td><code>date</code></td>
<td>日期</td>
<td>生日</td>
</tr>
<tr>
<td><code>datetime</code></td>
<td>日期时间</td>
<td>创建时间</td>
</tr>
<tr>
<td><code>daterange</code></td>
<td>日期范围</td>
<td>查询时间段</td>
</tr>
<tr>
<td><code>time</code></td>
<td>时间</td>
<td>开始时间</td>
</tr>
<tr>
<td><code>cascader</code></td>
<td>级联选择</td>
<td>省市区</td>
</tr>
<tr>
<td><code>tree-select</code></td>
<td>树选择</td>
<td>部门</td>
</tr>
<tr>
<td><code>upload</code></td>
<td>文件上传</td>
<td>头像、附件</td>
</tr>
<tr>
<td><code>editor</code></td>
<td>富文本</td>
<td>文章内容</td>
</tr>
</tbody>
</table>
<h3 id="request-请求配置" tabindex="-1">request 请求配置 <a class="header-anchor" href="#request-请求配置" aria-label="Permalink to &quot;request 请求配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  request: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 分页查询</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // query 包含: page, limit, 搜索条件</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">limit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">searchParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> query;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ current: page, size: limit, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">searchParams });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        records: res.data.records,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        total: res.data.total,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        currentPage: res.data.current,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        pageSize: res.data.size,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 新增</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 编辑</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 删除</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 详情（查看时获取）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    infoRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h2 id="高级功能" tabindex="-1">高级功能 <a class="header-anchor" href="#高级功能" aria-label="Permalink to &quot;高级功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="字典使用" tabindex="-1">字典使用 <a class="header-anchor" href="#字典使用" aria-label="Permalink to &quot;字典使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dict: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 方式一：静态数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 方式二：远程字典</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      url: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/api/dict/status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 方式三：自定义获取</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      getData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getDictData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'sys_status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> res.data;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="表单联动" tabindex="-1">表单联动 <a class="header-anchor" href="#表单联动" aria-label="Permalink to &quot;表单联动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  type: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'类型'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dict: { data: [{ label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'个人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }, { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'企业'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      valueChange</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 切换类型时，清空关联字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form.companyName </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form.idCard </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  companyName: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'公司名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 仅企业类型显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> form.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  idCard: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'身份证号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 仅个人类型显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> form.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="自定义列渲染" tabindex="-1">自定义列渲染 <a class="header-anchor" href="#自定义列渲染" aria-label="Permalink to &quot;自定义列渲染&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  avatar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'头像'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    column: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      cellRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">avatar</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> src</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">avatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">} />;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    column: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      cellRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> color</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'green'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'red'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> text</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '启用'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tag</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}>{text}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="自定义操作按钮" tabindex="-1">自定义操作按钮 <a class="header-anchor" href="#自定义操作按钮" aria-label="Permalink to &quot;自定义操作按钮&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowHandle: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      view: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 查看按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      edit: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 编辑按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      remove: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 删除按钮</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 自定义按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      audit: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'审核'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'primary'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> auditApi</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          crudExpose.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">doRefresh</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="工具栏配置" tabindex="-1">工具栏配置 <a class="header-anchor" href="#工具栏配置" aria-label="Permalink to &quot;工具栏配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  toolbar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      add: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 新增按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      refresh: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 刷新按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      export: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 导出按钮</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 自定义按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      import: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'导入'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ant-design:upload-outlined'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          // 打开导入弹窗</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="权限控制" tabindex="-1">权限控制 <a class="header-anchor" href="#权限控制" aria-label="Permalink to &quot;权限控制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  toolbar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      add: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:add'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowHandle: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      edit: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      remove: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:delete'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h2 id="完整示例" tabindex="-1">完整示例 <a class="header-anchor" href="#完整示例" aria-label="Permalink to &quot;完整示例&quot;">&ZeroWidthSpace;</a></h2>
<p>以下是一个用户管理页面的完整示例：</p>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { ref } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useFs, compute } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { message } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ant-design-vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, createUser, updateUser, deleteUser, resetPassword } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudExpose</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useFs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">limit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">params</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> query;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ current: page, size: limit, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">params });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { records: res.data.records, total: res.data.total };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建成功'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.id, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'更新成功'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'删除成功'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    toolbar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        add: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:add'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    rowHandle: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">220</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        view: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        edit: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        remove: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:delete'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        resetPwd: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'重置密码'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'link'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:resetPwd'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> resetPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'密码已重置为 123456'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      id: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      avatar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'头像'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'avatar-uploader'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          align: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'center'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        editForm: { disabled: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      nickname: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'昵称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入昵称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      email: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入有效的邮箱地址'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      mobile: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'手机号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [{ pattern:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">^</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">[3-9]\d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">{9}$</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入有效的手机号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      deptId: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'部门'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'tree-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          component: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            loadData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getDeptTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> res.data;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            fieldNames: { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'name'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          formatter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.deptName,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      roleIds: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'角色'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          component: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            mode: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'multiple'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            options</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getRoleList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> res.data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">item</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ label: item.name, value: item.id }));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dict: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      createTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"user-manage"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">crudBinding</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> scoped</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.user-manage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  padding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">16</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">px</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/frontend/permission.html">权限控制</a> - 前端权限控制详解</li>
<li><a href="/zh/guide/backend/">后端开发</a> - 学习后端 API 开发</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="crud-快速开发" tabindex="-1">CRUD 快速开发 <a class="header-anchor" href="#crud-快速开发" aria-label="Permalink to &quot;CRUD 快速开发&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握使用 Fast Crud 快速开发增删改查页面</p>
</div>
<h2 id="简介" tabindex="-1">简介 <a class="header-anchor" href="#简介" aria-label="Permalink to &quot;简介&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 集成了 <a href="http://fast-crud.docmirror.cn/" target="_blank" rel="noreferrer">Fast Crud</a>，只需几行代码即可完成 CRUD 页面开发。</p>
<h2 id="基础示例" tabindex="-1">基础示例 <a class="header-anchor" href="#基础示例" aria-label="Permalink to &quot;基础示例&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="最简示例" tabindex="-1">最简示例 <a class="header-anchor" href="#最简示例" aria-label="Permalink to &quot;最简示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useFs } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, createUser, updateUser, deleteUser } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useFs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { records: res.data.records, total: res.data.total };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.id, form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      email: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dict: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      createTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">crudBinding</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><p>只需要上面这段代码，就能得到一个完整的 CRUD 页面，包含：</p>
<ul>
<li>✅ 数据列表</li>
<li>✅ 分页</li>
<li>✅ 搜索</li>
<li>✅ 新增</li>
<li>✅ 编辑</li>
<li>✅ 删除</li>
</ul>
<h2 id="配置详解" tabindex="-1">配置详解 <a class="header-anchor" href="#配置详解" aria-label="Permalink to &quot;配置详解&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="columns-列配置" tabindex="-1">columns 列配置 <a class="header-anchor" href="#columns-列配置" aria-label="Permalink to &quot;columns 列配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 字段名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列标题</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段类型</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 列表配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    column: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">120</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 列宽</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      fixed: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'left'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 固定列</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      sortable: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否可排序</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      align: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'center'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 对齐方式</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      formatter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">context</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {}, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 格式化</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 表单配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      disabled: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否禁用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      rules: [],                  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 校验规则</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: {},              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      col: { span: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">12</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 栅格配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 搜索配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    search: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: {},              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 新增表单配置（覆盖 form）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    addForm: {},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 编辑表单配置（覆盖 form）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    editForm: {},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查看表单配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    viewForm: {},</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="字段类型" tabindex="-1">字段类型 <a class="header-anchor" href="#字段类型" aria-label="Permalink to &quot;字段类型&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>text</code></td>
<td>文本输入</td>
<td>用户名、标题</td>
</tr>
<tr>
<td><code>textarea</code></td>
<td>多行文本</td>
<td>备注、描述</td>
</tr>
<tr>
<td><code>number</code></td>
<td>数字输入</td>
<td>年龄、数量</td>
</tr>
<tr>
<td><code>password</code></td>
<td>密码输入</td>
<td>密码</td>
</tr>
<tr>
<td><code>select</code></td>
<td>下拉选择</td>
<td>状态</td>
</tr>
<tr>
<td><code>dict-select</code></td>
<td>字典下拉</td>
<td>带字典的选择</td>
</tr>
<tr>
<td><code>radio</code></td>
<td>单选</td>
<td>性别</td>
</tr>
<tr>
<td><code>checkbox</code></td>
<td>多选</td>
<td>爱好</td>
</tr>
<tr>
<td><code>switch</code></td>
<td>开关</td>
<td>是否启用</td>
</tr>
<tr>
<td><code>date</code></td>
<td>日期</td>
<td>生日</td>
</tr>
<tr>
<td><code>datetime</code></td>
<td>日期时间</td>
<td>创建时间</td>
</tr>
<tr>
<td><code>daterange</code></td>
<td>日期范围</td>
<td>查询时间段</td>
</tr>
<tr>
<td><code>time</code></td>
<td>时间</td>
<td>开始时间</td>
</tr>
<tr>
<td><code>cascader</code></td>
<td>级联选择</td>
<td>省市区</td>
</tr>
<tr>
<td><code>tree-select</code></td>
<td>树选择</td>
<td>部门</td>
</tr>
<tr>
<td><code>upload</code></td>
<td>文件上传</td>
<td>头像、附件</td>
</tr>
<tr>
<td><code>editor</code></td>
<td>富文本</td>
<td>文章内容</td>
</tr>
</tbody>
</table>
<h3 id="request-请求配置" tabindex="-1">request 请求配置 <a class="header-anchor" href="#request-请求配置" aria-label="Permalink to &quot;request 请求配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  request: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 分页查询</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // query 包含: page, limit, 搜索条件</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">limit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">searchParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> query;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ current: page, size: limit, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">searchParams });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        records: res.data.records,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        total: res.data.total,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        currentPage: res.data.current,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        pageSize: res.data.size,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 新增</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 编辑</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 删除</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 详情（查看时获取）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    infoRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h2 id="高级功能" tabindex="-1">高级功能 <a class="header-anchor" href="#高级功能" aria-label="Permalink to &quot;高级功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="字典使用" tabindex="-1">字典使用 <a class="header-anchor" href="#字典使用" aria-label="Permalink to &quot;字典使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dict: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 方式一：静态数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 方式二：远程字典</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      url: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/api/dict/status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 方式三：自定义获取</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      getData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getDictData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'sys_status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> res.data;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="表单联动" tabindex="-1">表单联动 <a class="header-anchor" href="#表单联动" aria-label="Permalink to &quot;表单联动&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  type: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'类型'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    dict: { data: [{ label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'个人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }, { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'企业'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      valueChange</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 切换类型时，清空关联字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form.companyName </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          form.idCard </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  companyName: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'公司名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 仅企业类型显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> form.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  idCard: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'身份证号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    form: {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 仅个人类型显示</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> form.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="自定义列渲染" tabindex="-1">自定义列渲染 <a class="header-anchor" href="#自定义列渲染" aria-label="Permalink to &quot;自定义列渲染&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  avatar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'头像'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    column: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      cellRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">avatar</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> src</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">avatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">} />;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    column: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      cellRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> color</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'green'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'red'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> text</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ?</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '启用'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> :</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tag</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}>{text}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="自定义操作按钮" tabindex="-1">自定义操作按钮 <a class="header-anchor" href="#自定义操作按钮" aria-label="Permalink to &quot;自定义操作按钮&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowHandle: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      view: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 查看按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      edit: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 编辑按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      remove: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 删除按钮</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 自定义按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      audit: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'审核'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'primary'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">          await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> auditApi</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          crudExpose.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">doRefresh</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="工具栏配置" tabindex="-1">工具栏配置 <a class="header-anchor" href="#工具栏配置" aria-label="Permalink to &quot;工具栏配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  toolbar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      add: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 新增按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      refresh: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 刷新按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      export: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 导出按钮</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 自定义按钮</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      import: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'导入'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ant-design:upload-outlined'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          // 打开导入弹窗</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h3 id="权限控制" tabindex="-1">权限控制 <a class="header-anchor" href="#权限控制" aria-label="Permalink to &quot;权限控制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> crudOptions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  toolbar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      add: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:add'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  rowHandle: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      edit: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      remove: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:delete'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h2 id="完整示例" tabindex="-1">完整示例 <a class="header-anchor" href="#完整示例" aria-label="Permalink to &quot;完整示例&quot;">&ZeroWidthSpace;</a></h2>
<p>以下是一个用户管理页面的完整示例：</p>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { ref } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useFs, compute } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { message } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ant-design-vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, createUser, updateUser, deleteUser, resetPassword } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudExpose</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useFs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">limit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">params</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> query;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ current: page, size: limit, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">params });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { records: res.data.records, total: res.data.total };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建成功'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.id, form);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'更新成功'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'删除成功'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    toolbar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        add: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:add'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    rowHandle: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">220</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      buttons: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        view: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        edit: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        remove: { show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:delete'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        resetPwd: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          text: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'重置密码'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'link'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          show: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:resetPwd'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> resetPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'密码已重置为 123456'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      id: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      avatar: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'头像'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'avatar-uploader'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          align: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'center'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        editForm: { disabled: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      nickname: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'昵称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入昵称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      email: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入有效的邮箱地址'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      mobile: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'手机号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          rules: [{ pattern:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">^</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">[3-9]\d</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">{9}$</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入有效的手机号'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      deptId: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'部门'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'tree-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          component: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            loadData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getDeptTree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> res.data;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            fieldNames: { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'name'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          formatter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> row.deptName,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      roleIds: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'角色'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          component: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            mode: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'multiple'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            options</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> res</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getRoleList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">              return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> res.data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">item</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ label: item.name, value: item.id }));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dict: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'启用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      createTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"user-manage"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">crudBinding</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> scoped</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.user-manage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  padding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">16</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">px</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/frontend/permission.html">权限控制</a> - 前端权限控制详解</li>
<li><a href="/zh/guide/backend/">后端开发</a> - 学习后端 API 开发</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[前端开发概述 ]]></title>
            <link>https://docs.battcn.com/zh/guide/frontend/</link>
            <guid>https://docs.battcn.com/zh/guide/frontend/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="前端开发概述" tabindex="-1">前端开发概述 <a class="header-anchor" href="#前端开发概述" aria-label="Permalink to &quot;前端开发概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 前端项目结构、开发规范和核心功能</p>
</div>
<h2 id="技术栈" tabindex="-1">技术栈 <a class="header-anchor" href="#技术栈" aria-label="Permalink to &quot;技术栈&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vue</td>
<td>3.x</td>
<td>渐进式 JavaScript 框架</td>
</tr>
<tr>
<td><strong>Vben Admin</strong></td>
<td><strong>5.x</strong></td>
<td>企业级中后台前端模板</td>
</tr>
<tr>
<td><strong>Fast Crud</strong></td>
<td><strong>1.x</strong></td>
<td>快速 CRUD 开发组件</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.x</td>
<td>JavaScript 超集，提供类型系统</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.x</td>
<td>企业级 UI 组件库</td>
</tr>
<tr>
<td>Vite</td>
<td>5.x</td>
<td>下一代前端构建工具</td>
</tr>
<tr>
<td>Pinia</td>
<td>最新版</td>
<td>Vue 3 状态管理</td>
</tr>
<tr>
<td>Vue Router</td>
<td>4.x</td>
<td>Vue 官方路由</td>
</tr>
<tr>
<td>Axios</td>
<td>最新版</td>
<td>HTTP 请求库</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="核心框架介绍" tabindex="-1">核心框架介绍 <a class="header-anchor" href="#核心框架介绍" aria-label="Permalink to &quot;核心框架介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="vben-admin-5-x" tabindex="-1">Vben Admin 5.x <a class="header-anchor" href="#vben-admin-5-x" aria-label="Permalink to &quot;Vben Admin 5.x&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://doc.vben.pro/" target="_blank" rel="noreferrer">Vben Admin</a> 是一个基于 Vue 3、Vite、TypeScript 的企业级中后台前端解决方案。</p>
<p><strong>核心特性</strong>：</p>
<ul>
<li><strong>Monorepo 架构</strong> — 基于 pnpm workspace + Turbo，支持多应用共享代码</li>
<li><strong>多 UI 组件库</strong> — 支持 Ant Design Vue、Element Plus、Naive UI 等</li>
<li><strong>权限系统</strong> — 完善的前端权限控制（路由/按钮/数据）</li>
<li><strong>主题定制</strong> — 支持暗黑模式、多主题色、布局切换</li>
<li><strong>国际化</strong> — 内置 i18n 多语言支持</li>
<li><strong>性能优化</strong> — 按需加载、代码分割、预渲染</li>
</ul>
<div class="tip custom-block"><p class="custom-block-title">官方文档</p>
<p>📖 <strong>Vben5 文档</strong>：<a href="https://doc.vben.pro/" target="_blank" rel="noreferrer">https://doc.vben.pro/</a></p>
<p>🔗 <strong>GitHub</strong>：<a href="https://github.com/vbenjs/vue-vben-admin" target="_blank" rel="noreferrer">https://github.com/vbenjs/vue-vben-admin</a></p>
</div>
<hr>
<h3 id="fast-crud" tabindex="-1">Fast Crud <a class="header-anchor" href="#fast-crud" aria-label="Permalink to &quot;Fast Crud&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://fast-crud.docmirror.cn/" target="_blank" rel="noreferrer">Fast Crud</a> 是一个快速开发 CRUD（增删改查）功能的 Vue 3 组件库。</p>
<p><strong>核心特性</strong>：</p>
<ul>
<li><strong>配置式开发</strong> — 通过 JSON 配置快速生成完整的 CRUD 页面</li>
<li><strong>零代码表格</strong> — 自动生成列表、搜索、分页</li>
<li><strong>表单联动</strong> — 支持字段联动、异步字典、级联选择</li>
<li><strong>多 UI 支持</strong> — 适配 Ant Design Vue、Element Plus、Naive UI</li>
<li><strong>可扩展</strong> — 支持自定义组件、插槽、事件</li>
</ul>
<div class="tip custom-block"><p class="custom-block-title">官方文档</p>
<p>📖 <strong>Fast Crud 文档</strong>：<a href="https://fast-crud.docmirror.cn/" target="_blank" rel="noreferrer">https://fast-crud.docmirror.cn/</a></p>
<p>🔗 <strong>GitHub</strong>：<a href="https://github.com/fast-crud/fast-crud" target="_blank" rel="noreferrer">https://github.com/fast-crud/fast-crud</a></p>
</div>
<p><strong>Fast Crud 基础示例</strong>：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { defineTableCrud } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/plugin/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineTableCrud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 请求配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.id, data),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.id),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 列配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      form: { rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      dict: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ url: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/dict/user_status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    createTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><p><strong>开发效率对比</strong>：</p>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>传统开发</th>
<th>Fast Crud</th>
</tr>
</thead>
<tbody>
<tr>
<td>列表页</td>
<td>50+ 行代码</td>
<td>10 行配置</td>
</tr>
<tr>
<td>搜索栏</td>
<td>手写表单</td>
<td>自动生成</td>
</tr>
<tr>
<td>新增/编辑表单</td>
<td>手写弹窗</td>
<td>自动生成</td>
</tr>
<tr>
<td>字典数据</td>
<td>手动请求</td>
<td><code>type: 'dict-select'</code></td>
</tr>
<tr>
<td>分页</td>
<td>手动处理</td>
<td>自动集成</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="项目结构" tabindex="-1">项目结构 <a class="header-anchor" href="#项目结构" aria-label="Permalink to &quot;项目结构&quot;">&ZeroWidthSpace;</a></h2>
<p>项目采用 <strong>Monorepo</strong> 架构，基于 <strong>Vben Admin 5.x</strong> 构建：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform-ui/</span></span>
<span class="line"><span>├── apps/                               # 📱 应用目录</span></span>
<span class="line"><span>│   └── web-antd/                       # Ant Design Vue 版本</span></span>
<span class="line"><span>│       ├── src/</span></span>
<span class="line"><span>│       │   ├── api/                    # 🌐 API 接口</span></span>
<span class="line"><span>│       │   │   ├── core/               # 核心接口</span></span>
<span class="line"><span>│       │   │   ├── system/             # 系统接口</span></span>
<span class="line"><span>│       │   │   └── workflow/           # 工作流接口</span></span>
<span class="line"><span>│       │   ├── views/                  # 📄 页面视图</span></span>
<span class="line"><span>│       │   │   ├── _core/              # 核心页面（登录等）</span></span>
<span class="line"><span>│       │   │   ├── dashboard/          # 仪表盘</span></span>
<span class="line"><span>│       │   │   └── wemirr/             # 业务页面</span></span>
<span class="line"><span>│       │   │       ├── system/         # 系统管理</span></span>
<span class="line"><span>│       │   │       ├── platform/       # 平台管理</span></span>
<span class="line"><span>│       │   │       ├── develop/        # 开发工具</span></span>
<span class="line"><span>│       │   │       ├── workflow/       # 工作流</span></span>
<span class="line"><span>│       │   │       ├── ai/             # AI 功能</span></span>
<span class="line"><span>│       │   │       ├── tms/            # 运输管理</span></span>
<span class="line"><span>│       │   │       └── wms/            # 仓储管理</span></span>
<span class="line"><span>│       │   ├── adapter/                # 适配器</span></span>
<span class="line"><span>│       │   ├── components/             # 业务组件</span></span>
<span class="line"><span>│       │   ├── layouts/                # 布局组件</span></span>
<span class="line"><span>│       │   ├── locales/                # 国际化</span></span>
<span class="line"><span>│       │   ├── plugin/                 # 插件</span></span>
<span class="line"><span>│       │   ├── router/                 # 路由配置</span></span>
<span class="line"><span>│       │   ├── store/                  # 状态管理</span></span>
<span class="line"><span>│       │   └── utils/                  # 工具函数</span></span>
<span class="line"><span>│       ├── .env.development            # 开发环境配置</span></span>
<span class="line"><span>│       ├── .env.production             # 生产环境配置</span></span>
<span class="line"><span>│       └── vite.config.mts             # Vite 配置</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── packages/                           # 📦 共享包</span></span>
<span class="line"><span>│   ├── @core/                          # 核心包</span></span>
<span class="line"><span>│   ├── constants/                      # 常量定义</span></span>
<span class="line"><span>│   ├── effects/                        # 副作用</span></span>
<span class="line"><span>│   ├── hooks/                          # 组合式函数</span></span>
<span class="line"><span>│   ├── icons/                          # 图标</span></span>
<span class="line"><span>│   ├── locales/                        # 国际化</span></span>
<span class="line"><span>│   ├── preferences/                    # 偏好设置</span></span>
<span class="line"><span>│   ├── stores/                         # 状态管理</span></span>
<span class="line"><span>│   ├── styles/                         # 样式</span></span>
<span class="line"><span>│   ├── types/                          # 类型定义</span></span>
<span class="line"><span>│   └── utils/                          # 工具函数</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── internal/                           # 🔧 内部工具</span></span>
<span class="line"><span>├── scripts/                            # 脚本</span></span>
<span class="line"><span>├── pnpm-workspace.yaml                 # Monorepo 配置</span></span>
<span class="line"><span>├── turbo.json                          # Turbo 配置</span></span>
<span class="line"><span>└── package.json                        # 依赖管理</span></span></code></pre>
</div><h2 id="开发环境" tabindex="-1">开发环境 <a class="header-anchor" href="#开发环境" aria-label="Permalink to &quot;开发环境&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="环境要求" tabindex="-1">环境要求 <a class="header-anchor" href="#环境要求" aria-label="Permalink to &quot;环境要求&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>Node.js</strong> &gt;= 18.0</li>
<li><strong>pnpm</strong> &gt;= 8.0</li>
<li><strong>Git</strong> &gt;= 2.0</li>
</ul>
<h3 id="安装步骤" tabindex="-1">安装步骤 <a class="header-anchor" href="#安装步骤" aria-label="Permalink to &quot;安装步骤&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 1. 克隆代码</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 2. 进入目录</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-ui</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 3. 安装 pnpm（如果没有）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 4. 安装依赖</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 5. 启动开发服务器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><h3 id="常用命令" tabindex="-1">常用命令 <a class="header-anchor" href="#常用命令" aria-label="Permalink to &quot;常用命令&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pnpm dev:antd</code></td>
<td>启动 Ant Design Vue 版本开发服务器</td>
</tr>
<tr>
<td><code>pnpm build:antd</code></td>
<td>打包 Ant Design Vue 版本</td>
</tr>
<tr>
<td><code>pnpm lint</code></td>
<td>运行 ESLint 检查</td>
</tr>
<tr>
<td><code>pnpm lint:fix</code></td>
<td>自动修复 ESLint 问题</td>
</tr>
<tr>
<td><code>pnpm type:check</code></td>
<td>TypeScript 类型检查</td>
</tr>
<tr>
<td><code>pnpm clean</code></td>
<td>清理构建产物和依赖</td>
</tr>
</tbody>
</table>
<h2 id="配置说明" tabindex="-1">配置说明 <a class="header-anchor" href="#配置说明" aria-label="Permalink to &quot;配置说明&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="环境变量" tabindex="-1">环境变量 <a class="header-anchor" href="#环境变量" aria-label="Permalink to &quot;环境变量&quot;">&ZeroWidthSpace;</a></h3>
<p>项目支持多环境配置，位于 <code>apps/web-antd/</code> 目录：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.env</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                  # 所有环境通用配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.env.development</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 开发环境配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.env.production</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 生产环境配置</span></span></code></pre>
</div><p>常用环境变量：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># .env.development</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># API 基础路径</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">VITE_GLOB_API_URL</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/api</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 是否开启 Mock</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">VITE_USE_MOCK</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">false</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Gitee 授权拦截（开发时关闭）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">VITE_GITEE_INTERCEPT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">false</span></span></code></pre>
</div><h3 id="代理配置" tabindex="-1">代理配置 <a class="header-anchor" href="#代理配置" aria-label="Permalink to &quot;代理配置&quot;">&ZeroWidthSpace;</a></h3>
<p>开发环境 API 代理配置在 <code>vite.config.ts</code>：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineConfig</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  server: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    proxy: {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      '/api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        target: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'http://localhost:9000'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 后端网关地址</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        changeOrigin: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        rewrite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> path.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">replace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">^</span><span style="--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold">\/</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">api</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h2 id="核心功能" tabindex="-1">核心功能 <a class="header-anchor" href="#核心功能" aria-label="Permalink to &quot;核心功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="路由与菜单" tabindex="-1">路由与菜单 <a class="header-anchor" href="#路由与菜单" aria-label="Permalink to &quot;路由与菜单&quot;">&ZeroWidthSpace;</a></h3>
<p>路由配置分为两种：</p>
<ol>
<li><strong>静态路由</strong> - 不需要权限的基础路由（登录、404等）</li>
<li><strong>动态路由</strong> - 根据用户权限从后端获取</li>
</ol>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 静态路由示例</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> staticRoutes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@/views/login/index.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    meta: { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'登录'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span></code></pre>
</div><h3 id="权限控制" tabindex="-1">权限控制 <a class="header-anchor" href="#权限控制" aria-label="Permalink to &quot;权限控制&quot;">&ZeroWidthSpace;</a></h3>
<p>前端权限控制包括：</p>
<ul>
<li><strong>路由权限</strong> - 控制页面访问</li>
<li><strong>按钮权限</strong> - 控制操作按钮显示</li>
<li><strong>数据权限</strong> - 配合后端控制数据范围</li>
</ul>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- 按钮权限示例 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:add'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>新增用户&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:edit'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>编辑&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:delete'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>删除&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="状态管理" tabindex="-1">状态管理 <a class="header-anchor" href="#状态管理" aria-label="Permalink to &quot;状态管理&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 Pinia 进行状态管理：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// stores/modules/user.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { defineStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'pinia'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> useUserStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  state</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userInfo: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    token: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  getters: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    getUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">state</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> state.userInfo,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  actions: {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> login</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">params</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 登录逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> logout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 登出逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h3 id="请求封装" tabindex="-1">请求封装 <a class="header-anchor" href="#请求封装" aria-label="Permalink to &quot;请求封装&quot;">&ZeroWidthSpace;</a></h3>
<p>HTTP 请求使用 Axios 封装：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// api/user.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { request } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/utils/request'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 获取用户列表</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">params</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserListParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">UserListResult</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/iam/users'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { params });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 创建用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreateUserParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/iam/users'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 更新用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UpdateUserParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/users/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 删除用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/users/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="开发规范" tabindex="-1">开发规范 <a class="header-anchor" href="#开发规范" aria-label="Permalink to &quot;开发规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="目录规范" tabindex="-1">目录规范 <a class="header-anchor" href="#目录规范" aria-label="Permalink to &quot;目录规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>views/</span></span>
<span class="line"><span>└── wemirr/</span></span>
<span class="line"><span>    └── system/              # 系统管理模块</span></span>
<span class="line"><span>        └── user/            # 用户管理页面</span></span>
<span class="line"><span>            ├── index.vue    # 页面入口</span></span>
<span class="line"><span>            ├── api.ts       # 页面 API</span></span>
<span class="line"><span>            ├── types.ts     # 类型定义</span></span>
<span class="line"><span>            └── components/  # 页面组件</span></span>
<span class="line"><span>                ├── UserForm.vue</span></span>
<span class="line"><span>                └── UserDetail.vue</span></span></code></pre>
</div><h3 id="命名规范" tabindex="-1">命名规范 <a class="header-anchor" href="#命名规范" aria-label="Permalink to &quot;命名规范&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>规范</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>文件夹</td>
<td>kebab-case</td>
<td><code>user-manage</code></td>
</tr>
<tr>
<td>Vue 组件</td>
<td>PascalCase</td>
<td><code>UserForm.vue</code></td>
</tr>
<tr>
<td>工具函数</td>
<td>camelCase</td>
<td><code>formatDate.ts</code></td>
</tr>
<tr>
<td>常量</td>
<td>UPPER_SNAKE_CASE</td>
<td><code>MAX_COUNT</code></td>
</tr>
<tr>
<td>CSS 类名</td>
<td>kebab-case</td>
<td><code>.user-form</code></td>
</tr>
</tbody>
</table>
<h3 id="代码规范" tabindex="-1">代码规范 <a class="header-anchor" href="#代码规范" aria-label="Permalink to &quot;代码规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 1. 导入语句</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { ref, reactive, onMounted } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useUserStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/store/modules/user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { UserInfo } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './types'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 2. 类型定义</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Props</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  userId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 3. Props / Emits</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> props</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Props</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> emit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineEmits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;{</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}>();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 4. 响应式数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> loading</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">UserInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 5. 计算属性</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> fullName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> `${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userInfo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">value</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">?.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">firstName</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">} ${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userInfo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">value</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">?.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lastName</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 6. 方法</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fetchUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  loading.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userInfo.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(props.userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    loading.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 7. 生命周期</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">onMounted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  fetchUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"user-detail"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-spin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">spinning</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">loading</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> v-if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userInfo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        {{ fullName }}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-spin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> scoped</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"less"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.user-detail {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  padding: 16px;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/frontend/components.html">组件使用</a> - 学习内置组件使用</li>
<li><a href="/zh/guide/frontend/crud.html">CRUD 开发</a> - 快速开发增删改查页面</li>
<li><a href="/zh/guide/frontend/permission.html">权限控制</a> - 深入理解前端权限</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="前端开发概述" tabindex="-1">前端开发概述 <a class="header-anchor" href="#前端开发概述" aria-label="Permalink to &quot;前端开发概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 前端项目结构、开发规范和核心功能</p>
</div>
<h2 id="技术栈" tabindex="-1">技术栈 <a class="header-anchor" href="#技术栈" aria-label="Permalink to &quot;技术栈&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vue</td>
<td>3.x</td>
<td>渐进式 JavaScript 框架</td>
</tr>
<tr>
<td><strong>Vben Admin</strong></td>
<td><strong>5.x</strong></td>
<td>企业级中后台前端模板</td>
</tr>
<tr>
<td><strong>Fast Crud</strong></td>
<td><strong>1.x</strong></td>
<td>快速 CRUD 开发组件</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.x</td>
<td>JavaScript 超集，提供类型系统</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.x</td>
<td>企业级 UI 组件库</td>
</tr>
<tr>
<td>Vite</td>
<td>5.x</td>
<td>下一代前端构建工具</td>
</tr>
<tr>
<td>Pinia</td>
<td>最新版</td>
<td>Vue 3 状态管理</td>
</tr>
<tr>
<td>Vue Router</td>
<td>4.x</td>
<td>Vue 官方路由</td>
</tr>
<tr>
<td>Axios</td>
<td>最新版</td>
<td>HTTP 请求库</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="核心框架介绍" tabindex="-1">核心框架介绍 <a class="header-anchor" href="#核心框架介绍" aria-label="Permalink to &quot;核心框架介绍&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="vben-admin-5-x" tabindex="-1">Vben Admin 5.x <a class="header-anchor" href="#vben-admin-5-x" aria-label="Permalink to &quot;Vben Admin 5.x&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://doc.vben.pro/" target="_blank" rel="noreferrer">Vben Admin</a> 是一个基于 Vue 3、Vite、TypeScript 的企业级中后台前端解决方案。</p>
<p><strong>核心特性</strong>：</p>
<ul>
<li><strong>Monorepo 架构</strong> — 基于 pnpm workspace + Turbo，支持多应用共享代码</li>
<li><strong>多 UI 组件库</strong> — 支持 Ant Design Vue、Element Plus、Naive UI 等</li>
<li><strong>权限系统</strong> — 完善的前端权限控制（路由/按钮/数据）</li>
<li><strong>主题定制</strong> — 支持暗黑模式、多主题色、布局切换</li>
<li><strong>国际化</strong> — 内置 i18n 多语言支持</li>
<li><strong>性能优化</strong> — 按需加载、代码分割、预渲染</li>
</ul>
<div class="tip custom-block"><p class="custom-block-title">官方文档</p>
<p>📖 <strong>Vben5 文档</strong>：<a href="https://doc.vben.pro/" target="_blank" rel="noreferrer">https://doc.vben.pro/</a></p>
<p>🔗 <strong>GitHub</strong>：<a href="https://github.com/vbenjs/vue-vben-admin" target="_blank" rel="noreferrer">https://github.com/vbenjs/vue-vben-admin</a></p>
</div>
<hr>
<h3 id="fast-crud" tabindex="-1">Fast Crud <a class="header-anchor" href="#fast-crud" aria-label="Permalink to &quot;Fast Crud&quot;">&ZeroWidthSpace;</a></h3>
<p><a href="https://fast-crud.docmirror.cn/" target="_blank" rel="noreferrer">Fast Crud</a> 是一个快速开发 CRUD（增删改查）功能的 Vue 3 组件库。</p>
<p><strong>核心特性</strong>：</p>
<ul>
<li><strong>配置式开发</strong> — 通过 JSON 配置快速生成完整的 CRUD 页面</li>
<li><strong>零代码表格</strong> — 自动生成列表、搜索、分页</li>
<li><strong>表单联动</strong> — 支持字段联动、异步字典、级联选择</li>
<li><strong>多 UI 支持</strong> — 适配 Ant Design Vue、Element Plus、Naive UI</li>
<li><strong>可扩展</strong> — 支持自定义组件、插槽、事件</li>
</ul>
<div class="tip custom-block"><p class="custom-block-title">官方文档</p>
<p>📖 <strong>Fast Crud 文档</strong>：<a href="https://fast-crud.docmirror.cn/" target="_blank" rel="noreferrer">https://fast-crud.docmirror.cn/</a></p>
<p>🔗 <strong>GitHub</strong>：<a href="https://github.com/fast-crud/fast-crud" target="_blank" rel="noreferrer">https://github.com/fast-crud/fast-crud</a></p>
</div>
<p><strong>Fast Crud 基础示例</strong>：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { defineTableCrud } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/plugin/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineTableCrud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 请求配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">update</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.id, data),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> api.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.id),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 列配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    username: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      form: { rules: [{ required: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'请输入用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }] },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      dict: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ url: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/dict/user_status'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    createTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      column: { width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">180</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      form: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><p><strong>开发效率对比</strong>：</p>
<table tabindex="0">
<thead>
<tr>
<th>功能</th>
<th>传统开发</th>
<th>Fast Crud</th>
</tr>
</thead>
<tbody>
<tr>
<td>列表页</td>
<td>50+ 行代码</td>
<td>10 行配置</td>
</tr>
<tr>
<td>搜索栏</td>
<td>手写表单</td>
<td>自动生成</td>
</tr>
<tr>
<td>新增/编辑表单</td>
<td>手写弹窗</td>
<td>自动生成</td>
</tr>
<tr>
<td>字典数据</td>
<td>手动请求</td>
<td><code>type: 'dict-select'</code></td>
</tr>
<tr>
<td>分页</td>
<td>手动处理</td>
<td>自动集成</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="项目结构" tabindex="-1">项目结构 <a class="header-anchor" href="#项目结构" aria-label="Permalink to &quot;项目结构&quot;">&ZeroWidthSpace;</a></h2>
<p>项目采用 <strong>Monorepo</strong> 架构，基于 <strong>Vben Admin 5.x</strong> 构建：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform-ui/</span></span>
<span class="line"><span>├── apps/                               # 📱 应用目录</span></span>
<span class="line"><span>│   └── web-antd/                       # Ant Design Vue 版本</span></span>
<span class="line"><span>│       ├── src/</span></span>
<span class="line"><span>│       │   ├── api/                    # 🌐 API 接口</span></span>
<span class="line"><span>│       │   │   ├── core/               # 核心接口</span></span>
<span class="line"><span>│       │   │   ├── system/             # 系统接口</span></span>
<span class="line"><span>│       │   │   └── workflow/           # 工作流接口</span></span>
<span class="line"><span>│       │   ├── views/                  # 📄 页面视图</span></span>
<span class="line"><span>│       │   │   ├── _core/              # 核心页面（登录等）</span></span>
<span class="line"><span>│       │   │   ├── dashboard/          # 仪表盘</span></span>
<span class="line"><span>│       │   │   └── wemirr/             # 业务页面</span></span>
<span class="line"><span>│       │   │       ├── system/         # 系统管理</span></span>
<span class="line"><span>│       │   │       ├── platform/       # 平台管理</span></span>
<span class="line"><span>│       │   │       ├── develop/        # 开发工具</span></span>
<span class="line"><span>│       │   │       ├── workflow/       # 工作流</span></span>
<span class="line"><span>│       │   │       ├── ai/             # AI 功能</span></span>
<span class="line"><span>│       │   │       ├── tms/            # 运输管理</span></span>
<span class="line"><span>│       │   │       └── wms/            # 仓储管理</span></span>
<span class="line"><span>│       │   ├── adapter/                # 适配器</span></span>
<span class="line"><span>│       │   ├── components/             # 业务组件</span></span>
<span class="line"><span>│       │   ├── layouts/                # 布局组件</span></span>
<span class="line"><span>│       │   ├── locales/                # 国际化</span></span>
<span class="line"><span>│       │   ├── plugin/                 # 插件</span></span>
<span class="line"><span>│       │   ├── router/                 # 路由配置</span></span>
<span class="line"><span>│       │   ├── store/                  # 状态管理</span></span>
<span class="line"><span>│       │   └── utils/                  # 工具函数</span></span>
<span class="line"><span>│       ├── .env.development            # 开发环境配置</span></span>
<span class="line"><span>│       ├── .env.production             # 生产环境配置</span></span>
<span class="line"><span>│       └── vite.config.mts             # Vite 配置</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── packages/                           # 📦 共享包</span></span>
<span class="line"><span>│   ├── @core/                          # 核心包</span></span>
<span class="line"><span>│   ├── constants/                      # 常量定义</span></span>
<span class="line"><span>│   ├── effects/                        # 副作用</span></span>
<span class="line"><span>│   ├── hooks/                          # 组合式函数</span></span>
<span class="line"><span>│   ├── icons/                          # 图标</span></span>
<span class="line"><span>│   ├── locales/                        # 国际化</span></span>
<span class="line"><span>│   ├── preferences/                    # 偏好设置</span></span>
<span class="line"><span>│   ├── stores/                         # 状态管理</span></span>
<span class="line"><span>│   ├── styles/                         # 样式</span></span>
<span class="line"><span>│   ├── types/                          # 类型定义</span></span>
<span class="line"><span>│   └── utils/                          # 工具函数</span></span>
<span class="line"><span>│</span></span>
<span class="line"><span>├── internal/                           # 🔧 内部工具</span></span>
<span class="line"><span>├── scripts/                            # 脚本</span></span>
<span class="line"><span>├── pnpm-workspace.yaml                 # Monorepo 配置</span></span>
<span class="line"><span>├── turbo.json                          # Turbo 配置</span></span>
<span class="line"><span>└── package.json                        # 依赖管理</span></span></code></pre>
</div><h2 id="开发环境" tabindex="-1">开发环境 <a class="header-anchor" href="#开发环境" aria-label="Permalink to &quot;开发环境&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="环境要求" tabindex="-1">环境要求 <a class="header-anchor" href="#环境要求" aria-label="Permalink to &quot;环境要求&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>Node.js</strong> &gt;= 18.0</li>
<li><strong>pnpm</strong> &gt;= 8.0</li>
<li><strong>Git</strong> &gt;= 2.0</li>
</ul>
<h3 id="安装步骤" tabindex="-1">安装步骤 <a class="header-anchor" href="#安装步骤" aria-label="Permalink to &quot;安装步骤&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 1. 克隆代码</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 2. 进入目录</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-ui</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 3. 安装 pnpm（如果没有）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 4. 安装依赖</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 5. 启动开发服务器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><h3 id="常用命令" tabindex="-1">常用命令 <a class="header-anchor" href="#常用命令" aria-label="Permalink to &quot;常用命令&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pnpm dev:antd</code></td>
<td>启动 Ant Design Vue 版本开发服务器</td>
</tr>
<tr>
<td><code>pnpm build:antd</code></td>
<td>打包 Ant Design Vue 版本</td>
</tr>
<tr>
<td><code>pnpm lint</code></td>
<td>运行 ESLint 检查</td>
</tr>
<tr>
<td><code>pnpm lint:fix</code></td>
<td>自动修复 ESLint 问题</td>
</tr>
<tr>
<td><code>pnpm type:check</code></td>
<td>TypeScript 类型检查</td>
</tr>
<tr>
<td><code>pnpm clean</code></td>
<td>清理构建产物和依赖</td>
</tr>
</tbody>
</table>
<h2 id="配置说明" tabindex="-1">配置说明 <a class="header-anchor" href="#配置说明" aria-label="Permalink to &quot;配置说明&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="环境变量" tabindex="-1">环境变量 <a class="header-anchor" href="#环境变量" aria-label="Permalink to &quot;环境变量&quot;">&ZeroWidthSpace;</a></h3>
<p>项目支持多环境配置，位于 <code>apps/web-antd/</code> 目录：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.env</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                  # 所有环境通用配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.env.development</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 开发环境配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.env.production</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 生产环境配置</span></span></code></pre>
</div><p>常用环境变量：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># .env.development</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># API 基础路径</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">VITE_GLOB_API_URL</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/api</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 是否开启 Mock</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">VITE_USE_MOCK</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">false</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Gitee 授权拦截（开发时关闭）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">VITE_GITEE_INTERCEPT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">false</span></span></code></pre>
</div><h3 id="代理配置" tabindex="-1">代理配置 <a class="header-anchor" href="#代理配置" aria-label="Permalink to &quot;代理配置&quot;">&ZeroWidthSpace;</a></h3>
<p>开发环境 API 代理配置在 <code>vite.config.ts</code>：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineConfig</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  server: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    proxy: {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">      '/api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        target: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'http://localhost:9000'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 后端网关地址</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        changeOrigin: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        rewrite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> path.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">replace</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">^</span><span style="--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold">\/</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF">api</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h2 id="核心功能" tabindex="-1">核心功能 <a class="header-anchor" href="#核心功能" aria-label="Permalink to &quot;核心功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="路由与菜单" tabindex="-1">路由与菜单 <a class="header-anchor" href="#路由与菜单" aria-label="Permalink to &quot;路由与菜单&quot;">&ZeroWidthSpace;</a></h3>
<p>路由配置分为两种：</p>
<ol>
<li><strong>静态路由</strong> - 不需要权限的基础路由（登录、404等）</li>
<li><strong>动态路由</strong> - 根据用户权限从后端获取</li>
</ol>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 静态路由示例</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> staticRoutes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@/views/login/index.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    meta: { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'登录'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span></code></pre>
</div><h3 id="权限控制" tabindex="-1">权限控制 <a class="header-anchor" href="#权限控制" aria-label="Permalink to &quot;权限控制&quot;">&ZeroWidthSpace;</a></h3>
<p>前端权限控制包括：</p>
<ul>
<li><strong>路由权限</strong> - 控制页面访问</li>
<li><strong>按钮权限</strong> - 控制操作按钮显示</li>
<li><strong>数据权限</strong> - 配合后端控制数据范围</li>
</ul>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- 按钮权限示例 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:add'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>新增用户&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:edit'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>编辑&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:delete'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>删除&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="状态管理" tabindex="-1">状态管理 <a class="header-anchor" href="#状态管理" aria-label="Permalink to &quot;状态管理&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 Pinia 进行状态管理：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// stores/modules/user.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { defineStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'pinia'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> useUserStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  state</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userInfo: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    token: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  getters: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    getUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">state</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> state.userInfo,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  actions: {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> login</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">params</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 登录逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> logout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 登出逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h3 id="请求封装" tabindex="-1">请求封装 <a class="header-anchor" href="#请求封装" aria-label="Permalink to &quot;请求封装&quot;">&ZeroWidthSpace;</a></h3>
<p>HTTP 请求使用 Axios 封装：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// api/user.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { request } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/utils/request'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 获取用户列表</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">params</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserListParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">UserListResult</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/iam/users'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { params });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 创建用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreateUserParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/iam/users'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 更新用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UpdateUserParams</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/users/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 删除用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`/iam/users/${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">id</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="开发规范" tabindex="-1">开发规范 <a class="header-anchor" href="#开发规范" aria-label="Permalink to &quot;开发规范&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="目录规范" tabindex="-1">目录规范 <a class="header-anchor" href="#目录规范" aria-label="Permalink to &quot;目录规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>views/</span></span>
<span class="line"><span>└── wemirr/</span></span>
<span class="line"><span>    └── system/              # 系统管理模块</span></span>
<span class="line"><span>        └── user/            # 用户管理页面</span></span>
<span class="line"><span>            ├── index.vue    # 页面入口</span></span>
<span class="line"><span>            ├── api.ts       # 页面 API</span></span>
<span class="line"><span>            ├── types.ts     # 类型定义</span></span>
<span class="line"><span>            └── components/  # 页面组件</span></span>
<span class="line"><span>                ├── UserForm.vue</span></span>
<span class="line"><span>                └── UserDetail.vue</span></span></code></pre>
</div><h3 id="命名规范" tabindex="-1">命名规范 <a class="header-anchor" href="#命名规范" aria-label="Permalink to &quot;命名规范&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>类型</th>
<th>规范</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>文件夹</td>
<td>kebab-case</td>
<td><code>user-manage</code></td>
</tr>
<tr>
<td>Vue 组件</td>
<td>PascalCase</td>
<td><code>UserForm.vue</code></td>
</tr>
<tr>
<td>工具函数</td>
<td>camelCase</td>
<td><code>formatDate.ts</code></td>
</tr>
<tr>
<td>常量</td>
<td>UPPER_SNAKE_CASE</td>
<td><code>MAX_COUNT</code></td>
</tr>
<tr>
<td>CSS 类名</td>
<td>kebab-case</td>
<td><code>.user-form</code></td>
</tr>
</tbody>
</table>
<h3 id="代码规范" tabindex="-1">代码规范 <a class="header-anchor" href="#代码规范" aria-label="Permalink to &quot;代码规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 1. 导入语句</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { ref, reactive, onMounted } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useUserStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/store/modules/user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { UserInfo } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './types'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 2. 类型定义</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Props</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  userId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 3. Props / Emits</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> props</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineProps</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Props</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> emit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineEmits</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;{</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}>();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 4. 响应式数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> loading</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">UserInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 5. 计算属性</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> fullName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> `${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userInfo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">value</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">?.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">firstName</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">} ${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userInfo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">value</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">?.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lastName</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 6. 方法</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fetchUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  loading.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userInfo.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(props.userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    loading.value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 7. 生命周期</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">onMounted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  fetchUserInfo</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"user-detail"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-spin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">spinning</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">loading</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> v-if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userInfo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        {{ fullName }}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-spin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> scoped</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"less"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.user-detail {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  padding: 16px;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/frontend/components.html">组件使用</a> - 学习内置组件使用</li>
<li><a href="/zh/guide/frontend/crud.html">CRUD 开发</a> - 快速开发增删改查页面</li>
<li><a href="/zh/guide/frontend/permission.html">权限控制</a> - 深入理解前端权限</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[前端权限控制 ]]></title>
            <link>https://docs.battcn.com/zh/guide/frontend/permission.html</link>
            <guid>https://docs.battcn.com/zh/guide/frontend/permission.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="前端权限控制" tabindex="-1">前端权限控制 <a class="header-anchor" href="#前端权限控制" aria-label="Permalink to &quot;前端权限控制&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 前端权限控制的实现方式</p>
</div>
<h2 id="权限模型" tabindex="-1">权限模型 <a class="header-anchor" href="#权限模型" aria-label="Permalink to &quot;权限模型&quot;">&ZeroWidthSpace;</a></h2>
<p>前端权限控制分为三个层级：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│              按钮权限                     │</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></code></pre>
</div><h2 id="路由权限" tabindex="-1">路由权限 <a class="header-anchor" href="#路由权限" aria-label="Permalink to &quot;路由权限&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="权限获取流程" tabindex="-1">权限获取流程 <a class="header-anchor" href="#权限获取流程" aria-label="Permalink to &quot;权限获取流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>用户登录 → 获取用户信息 → 获取权限列表 → 动态生成路由 → 渲染菜单</span></span></code></pre>
</div><h3 id="路由配置" tabindex="-1">路由配置 <a class="header-anchor" href="#路由配置" aria-label="Permalink to &quot;路由配置&quot;">&ZeroWidthSpace;</a></h3>
<p>路由权限通过后端返回的菜单数据动态生成：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 后端返回的菜单数据结构</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MenuItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  parentId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 路由名称</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 路由路径</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  component</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件路径</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  redirect</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 重定向</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  meta</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 菜单标题</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    icon</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 菜单图标</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    hidden</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否隐藏</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    keepAlive</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否缓存</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[]; </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 权限标识</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  children</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MenuItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="路由守卫" tabindex="-1">路由守卫 <a class="header-anchor" href="#路由守卫" aria-label="Permalink to &quot;路由守卫&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// router/guard.ts</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">router.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">beforeEach</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">to</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">from</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> token</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getToken;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 白名单路由直接放行</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (whiteList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(to.path)) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 未登录跳转登录页</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">token) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, query: { redirect: to.fullPath } });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 已登录但未获取用户信息</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userStore.getUserInfo) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 获取用户信息和权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserInfoAction</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 动态添加路由</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> routes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissionStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildRoutesAction</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      routes.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forEach</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">route</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> router.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addRoute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(route));</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 重新进入当前路由</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">to, replace: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (error) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">logout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h2 id="按钮权限" tabindex="-1">按钮权限 <a class="header-anchor" href="#按钮权限" aria-label="Permalink to &quot;按钮权限&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="权限指令" tabindex="-1">权限指令 <a class="header-anchor" href="#权限指令" aria-label="Permalink to &quot;权限指令&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 <code>v-auth</code> 指令控制按钮显示：</p>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 单个权限 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:add'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>新增用户&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 多个权限（满足任一即可） --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:update'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>编辑&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 必须满足所有权限 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:audit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>审核编辑&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="指令实现" tabindex="-1">指令实现 <a class="header-anchor" href="#指令实现" aria-label="Permalink to &quot;指令实现&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// directives/auth.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { Directive, DirectiveBinding } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> auth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Directive</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  mounted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">el</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Element</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">binding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DirectiveBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">modifiers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> binding;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">value) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Array.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isArray</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [value];</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> hasAuth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> modifiers.all</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">every</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p))</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">some</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">hasAuth) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      el.parentNode?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">removeChild</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(el);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> auth;</span></span></code></pre>
</div><h3 id="函数式判断" tabindex="-1">函数式判断 <a class="header-anchor" href="#函数式判断" aria-label="Permalink to &quot;函数式判断&quot;">&ZeroWidthSpace;</a></h3>
<p>在 JS/TS 中判断权限：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 单个权限判断</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:add'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 多个权限判断（任一满足）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:update'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">])) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 多个权限判断（全部满足）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:audit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">], </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="usepermission-hook" tabindex="-1">usePermission Hook <a class="header-anchor" href="#usepermission-hook" aria-label="Permalink to &quot;usePermission Hook&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// hooks/web/usePermission.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useUserStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/store/modules/user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * 判断是否有权限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 权限标识，可以是字符串或数组</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> all</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 是否需要全部满足，默认 false（满足任一即可）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    permission</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[],</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    all</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  )</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getPermissions;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">permission) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">permissions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">length</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 超级管理员拥有所有权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'*:*:*'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissionList</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Array.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isArray</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(permission) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [permission];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> all</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissionList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">every</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p))</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissionList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">some</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * 判断是否有角色</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasRole</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">role</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[], </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">all</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> roles</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getRoles;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">role) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roles.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">length</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> roleList</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Array.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isArray</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> role </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [role];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> all</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">every</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roles.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(r))</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">some</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roles.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(r));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    hasPermission,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    hasRole,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="菜单配置" tabindex="-1">菜单配置 <a class="header-anchor" href="#菜单配置" aria-label="Permalink to &quot;菜单配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="后台菜单配置" tabindex="-1">后台菜单配置 <a class="header-anchor" href="#后台菜单配置" aria-label="Permalink to &quot;后台菜单配置&quot;">&ZeroWidthSpace;</a></h3>
<p>在「系统管理」-「菜单管理」中配置菜单：</p>
<table tabindex="0">
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>菜单名称</td>
<td>显示的标题</td>
<td>用户管理</td>
</tr>
<tr>
<td>路由地址</td>
<td>前端路由路径</td>
<td>/system/user</td>
</tr>
<tr>
<td>组件路径</td>
<td>Vue 组件路径</td>
<td>wemirr/system/user/index</td>
</tr>
<tr>
<td>权限标识</td>
<td>权限编码</td>
<td>system:user:list</td>
</tr>
<tr>
<td>图标</td>
<td>菜单图标</td>
<td>ant-design:user-outlined</td>
</tr>
</tbody>
</table>
<h3 id="按钮权限配置" tabindex="-1">按钮权限配置 <a class="header-anchor" href="#按钮权限配置" aria-label="Permalink to &quot;按钮权限配置&quot;">&ZeroWidthSpace;</a></h3>
<p>按钮作为菜单的子节点配置：</p>
<table tabindex="0">
<thead>
<tr>
<th>按钮名称</th>
<th>权限标识</th>
</tr>
</thead>
<tbody>
<tr>
<td>新增</td>
<td>system:user:add</td>
</tr>
<tr>
<td>编辑</td>
<td>system:user:edit</td>
</tr>
<tr>
<td>删除</td>
<td>system:user:delete</td>
</tr>
<tr>
<td>导出</td>
<td>system:user:export</td>
</tr>
</tbody>
</table>
<h3 id="权限标识规范" tabindex="-1">权限标识规范 <a class="header-anchor" href="#权限标识规范" aria-label="Permalink to &quot;权限标识规范&quot;">&ZeroWidthSpace;</a></h3>
<p>推荐使用三级命名：<code>模块:资源:操作</code></p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>system:user:list     # 系统模块-用户-列表</span></span>
<span class="line"><span>system:user:add      # 系统模块-用户-新增</span></span>
<span class="line"><span>system:user:edit     # 系统模块-用户-编辑</span></span>
<span class="line"><span>system:user:delete   # 系统模块-用户-删除</span></span>
<span class="line"><span>system:user:export   # 系统模块-用户-导出</span></span>
<span class="line"><span>system:role:list     # 系统模块-角色-列表</span></span></code></pre>
</div><h2 id="实战示例" tabindex="-1">实战示例 <a class="header-anchor" href="#实战示例" aria-label="Permalink to &quot;实战示例&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="完整页面示例" tabindex="-1">完整页面示例 <a class="header-anchor" href="#完整页面示例" aria-label="Permalink to &quot;完整页面示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { ref } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, deleteUser } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据权限控制列表列显示</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> baseColumns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有查看薪资权限才显示薪资列</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:salary:view'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    baseColumns.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'薪资'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'salary'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseColumns;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据权限控制操作按钮</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> actionColumn</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'操作'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  key: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'action'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  customRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">space</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      {</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">('</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">user</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">view</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">') &#x26;&#x26; </span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> onClick</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleView</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          查看</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      )}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      {</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">('</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">user</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">edit</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">') &#x26;&#x26; </span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> onClick</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleEdit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          编辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      )}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      {</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">('</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">user</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">delete</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">') &#x26;&#x26; </span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">popconfirm</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"确定删除？"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> onConfirm</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleDelete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> danger</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">删除</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">button</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">popconfirm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      )}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">space</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"user-list"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    &#x3C;!-- 工具栏 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"toolbar"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:add'"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"primary"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleAdd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        新增用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:export'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleExport</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        导出</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:import'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleImport</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        导入</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    &#x3C;!-- 数据表格 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">columns, actionColumn]</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">data-source</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">dataSource</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="动态表单控制" tabindex="-1">动态表单控制 <a class="header-anchor" href="#动态表单控制" aria-label="Permalink to &quot;动态表单控制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据权限动态生成表单项</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> formSchema</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> schema</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有管理员权限才能分配角色</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:assignRole'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    schema.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'roleIds'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'角色'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      componentProps: { mode: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'multiple'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有设置薪资权限才显示薪资字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:salary:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    schema.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'salary'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'薪资'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'InputNumber'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> schema;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-权限粒度" tabindex="-1">1. 权限粒度 <a class="header-anchor" href="#_1-权限粒度" aria-label="Permalink to &quot;1. 权限粒度&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>粗粒度</strong>：菜单级别，控制页面访问</li>
<li><strong>细粒度</strong>：按钮级别，控制具体操作</li>
<li><strong>数据粒度</strong>：行级别，由后端控制</li>
</ul>
<h3 id="_2-权限缓存" tabindex="-1">2. 权限缓存 <a class="header-anchor" href="#_2-权限缓存" aria-label="Permalink to &quot;2. 权限缓存&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 登录后缓存权限，避免重复请求</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 权限存储在 Pinia 中</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setPermissions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(permissions);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 需要时从 store 获取</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getPermissions;</span></span></code></pre>
</div><h3 id="_3-权限刷新" tabindex="-1">3. 权限刷新 <a class="header-anchor" href="#_3-权限刷新" aria-label="Permalink to &quot;3. 权限刷新&quot;">&ZeroWidthSpace;</a></h3>
<p>用户权限变更后需要刷新：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 方式一：重新登录</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">logout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">router.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 方式二：重新获取权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserInfoAction</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 刷新页面以重新生成路由</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">window.location.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span></code></pre>
</div><h3 id="_4-超级管理员" tabindex="-1">4. 超级管理员 <a class="header-anchor" href="#_4-超级管理员" aria-label="Permalink to &quot;4. 超级管理员&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 超级管理员拥有所有权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'*:*:*'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/backend/">后端开发</a> - 学习后端权限控制</li>
<li><a href="/zh/guide/packages/data_permission.html">数据权限</a> - 了解数据级权限控制</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="前端权限控制" tabindex="-1">前端权限控制 <a class="header-anchor" href="#前端权限控制" aria-label="Permalink to &quot;前端权限控制&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 前端权限控制的实现方式</p>
</div>
<h2 id="权限模型" tabindex="-1">权限模型 <a class="header-anchor" href="#权限模型" aria-label="Permalink to &quot;权限模型&quot;">&ZeroWidthSpace;</a></h2>
<p>前端权限控制分为三个层级：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│              按钮权限                     │</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></code></pre>
</div><h2 id="路由权限" tabindex="-1">路由权限 <a class="header-anchor" href="#路由权限" aria-label="Permalink to &quot;路由权限&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="权限获取流程" tabindex="-1">权限获取流程 <a class="header-anchor" href="#权限获取流程" aria-label="Permalink to &quot;权限获取流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>用户登录 → 获取用户信息 → 获取权限列表 → 动态生成路由 → 渲染菜单</span></span></code></pre>
</div><h3 id="路由配置" tabindex="-1">路由配置 <a class="header-anchor" href="#路由配置" aria-label="Permalink to &quot;路由配置&quot;">&ZeroWidthSpace;</a></h3>
<p>路由权限通过后端返回的菜单数据动态生成：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 后端返回的菜单数据结构</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MenuItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  parentId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 路由名称</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 路由路径</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  component</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 组件路径</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  redirect</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 重定向</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  meta</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 菜单标题</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    icon</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;        </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 菜单图标</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    hidden</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否隐藏</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    keepAlive</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 是否缓存</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[]; </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 权限标识</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  children</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MenuItem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="路由守卫" tabindex="-1">路由守卫 <a class="header-anchor" href="#路由守卫" aria-label="Permalink to &quot;路由守卫&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// router/guard.ts</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">router.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">beforeEach</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">to</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">from</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> token</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getToken;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 白名单路由直接放行</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (whiteList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(to.path)) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 未登录跳转登录页</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">token) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, query: { redirect: to.fullPath } });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 已登录但未获取用户信息</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userStore.getUserInfo) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 获取用户信息和权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserInfoAction</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 动态添加路由</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> routes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissionStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildRoutesAction</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      routes.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forEach</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">route</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> router.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addRoute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(route));</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      // 重新进入当前路由</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">to, replace: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">catch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (error) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">logout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  next</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h2 id="按钮权限" tabindex="-1">按钮权限 <a class="header-anchor" href="#按钮权限" aria-label="Permalink to &quot;按钮权限&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="权限指令" tabindex="-1">权限指令 <a class="header-anchor" href="#权限指令" aria-label="Permalink to &quot;权限指令&quot;">&ZeroWidthSpace;</a></h3>
<p>使用 <code>v-auth</code> 指令控制按钮显示：</p>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 单个权限 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:add'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>新增用户&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 多个权限（满足任一即可） --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:update'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>编辑&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  &#x3C;!-- 必须满足所有权限 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:audit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>审核编辑&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="指令实现" tabindex="-1">指令实现 <a class="header-anchor" href="#指令实现" aria-label="Permalink to &quot;指令实现&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// directives/auth.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { Directive, DirectiveBinding } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> auth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Directive</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  mounted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">el</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Element</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">binding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DirectiveBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">modifiers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> binding;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">value) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Array.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isArray</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(value) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> value </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [value];</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> hasAuth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> modifiers.all</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">every</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p))</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">some</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">hasAuth) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      el.parentNode?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">removeChild</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(el);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> auth;</span></span></code></pre>
</div><h3 id="函数式判断" tabindex="-1">函数式判断 <a class="header-anchor" href="#函数式判断" aria-label="Permalink to &quot;函数式判断&quot;">&ZeroWidthSpace;</a></h3>
<p>在 JS/TS 中判断权限：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 单个权限判断</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:add'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 多个权限判断（任一满足）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:update'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">])) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 多个权限判断（全部满足）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:audit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">], </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="usepermission-hook" tabindex="-1">usePermission Hook <a class="header-anchor" href="#usepermission-hook" aria-label="Permalink to &quot;usePermission Hook&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// hooks/web/usePermission.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useUserStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/store/modules/user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * 判断是否有权限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 权限标识，可以是字符串或数组</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> all</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 是否需要全部满足，默认 false（满足任一即可）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    permission</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[],</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    all</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  )</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getPermissions;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">permission) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">permissions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">length</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 超级管理员拥有所有权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'*:*:*'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissionList</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Array.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isArray</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(permission) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [permission];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> all</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissionList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">every</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p))</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissionList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">some</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(p));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   * 判断是否有角色</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> hasRole</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">role</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[], </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">all</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> roles</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getRoles;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">role) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roles.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">length</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> roleList</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Array.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isArray</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> role </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [role];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> all</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      ?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">every</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roles.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(r))</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      :</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleList.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">some</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roles.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(r));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    hasPermission,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    hasRole,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="菜单配置" tabindex="-1">菜单配置 <a class="header-anchor" href="#菜单配置" aria-label="Permalink to &quot;菜单配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="后台菜单配置" tabindex="-1">后台菜单配置 <a class="header-anchor" href="#后台菜单配置" aria-label="Permalink to &quot;后台菜单配置&quot;">&ZeroWidthSpace;</a></h3>
<p>在「系统管理」-「菜单管理」中配置菜单：</p>
<table tabindex="0">
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>菜单名称</td>
<td>显示的标题</td>
<td>用户管理</td>
</tr>
<tr>
<td>路由地址</td>
<td>前端路由路径</td>
<td>/system/user</td>
</tr>
<tr>
<td>组件路径</td>
<td>Vue 组件路径</td>
<td>wemirr/system/user/index</td>
</tr>
<tr>
<td>权限标识</td>
<td>权限编码</td>
<td>system:user:list</td>
</tr>
<tr>
<td>图标</td>
<td>菜单图标</td>
<td>ant-design:user-outlined</td>
</tr>
</tbody>
</table>
<h3 id="按钮权限配置" tabindex="-1">按钮权限配置 <a class="header-anchor" href="#按钮权限配置" aria-label="Permalink to &quot;按钮权限配置&quot;">&ZeroWidthSpace;</a></h3>
<p>按钮作为菜单的子节点配置：</p>
<table tabindex="0">
<thead>
<tr>
<th>按钮名称</th>
<th>权限标识</th>
</tr>
</thead>
<tbody>
<tr>
<td>新增</td>
<td>system:user:add</td>
</tr>
<tr>
<td>编辑</td>
<td>system:user:edit</td>
</tr>
<tr>
<td>删除</td>
<td>system:user:delete</td>
</tr>
<tr>
<td>导出</td>
<td>system:user:export</td>
</tr>
</tbody>
</table>
<h3 id="权限标识规范" tabindex="-1">权限标识规范 <a class="header-anchor" href="#权限标识规范" aria-label="Permalink to &quot;权限标识规范&quot;">&ZeroWidthSpace;</a></h3>
<p>推荐使用三级命名：<code>模块:资源:操作</code></p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>system:user:list     # 系统模块-用户-列表</span></span>
<span class="line"><span>system:user:add      # 系统模块-用户-新增</span></span>
<span class="line"><span>system:user:edit     # 系统模块-用户-编辑</span></span>
<span class="line"><span>system:user:delete   # 系统模块-用户-删除</span></span>
<span class="line"><span>system:user:export   # 系统模块-用户-导出</span></span>
<span class="line"><span>system:role:list     # 系统模块-角色-列表</span></span></code></pre>
</div><h2 id="实战示例" tabindex="-1">实战示例 <a class="header-anchor" href="#实战示例" aria-label="Permalink to &quot;实战示例&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="完整页面示例" tabindex="-1">完整页面示例 <a class="header-anchor" href="#完整页面示例" aria-label="Permalink to &quot;完整页面示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { ref } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getUserList, deleteUser } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据权限控制列表列显示</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> baseColumns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有查看薪资权限才显示薪资列</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:salary:view'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    baseColumns.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'薪资'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dataIndex: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'salary'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseColumns;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据权限控制操作按钮</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> actionColumn</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'操作'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  key: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'action'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  width: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  customRender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">space</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      {</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">('</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">user</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">view</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">') &#x26;&#x26; </span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> onClick</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleView</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          查看</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      )}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      {</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">('</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">user</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">edit</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">') &#x26;&#x26; </span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> onClick</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleEdit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          编辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      )}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      {</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">('</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">user</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">delete</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">') &#x26;&#x26; </span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">popconfirm</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"确定删除？"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> onConfirm</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handleDelete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">record</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          &#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">button</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"link"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> danger</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">删除</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">button</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#x3C;/</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">-</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">popconfirm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      )}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">a</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">space</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"user-list"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    &#x3C;!-- 工具栏 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"toolbar"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:add'"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"primary"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleAdd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        新增用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:export'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleExport</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        导出</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"'user:import'"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">handleImport</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        导入</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    &#x3C;!-- 数据表格 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a-table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">columns, actionColumn]</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> :</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">data-source</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">dataSource</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="动态表单控制" tabindex="-1">动态表单控制 <a class="header-anchor" href="#动态表单控制" aria-label="Permalink to &quot;动态表单控制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { usePermission } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/hooks/web/usePermission'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> usePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据权限动态生成表单项</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> formSchema</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> computed</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> schema</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'username'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'用户名'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    { field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'邮箱'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Input'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有管理员权限才能分配角色</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:assignRole'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    schema.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'roleIds'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'角色'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      componentProps: { mode: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'multiple'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 有设置薪资权限才显示薪资字段</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hasPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user:salary:edit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    schema.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      field: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'salary'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'薪资'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      component: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'InputNumber'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> schema;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-权限粒度" tabindex="-1">1. 权限粒度 <a class="header-anchor" href="#_1-权限粒度" aria-label="Permalink to &quot;1. 权限粒度&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>粗粒度</strong>：菜单级别，控制页面访问</li>
<li><strong>细粒度</strong>：按钮级别，控制具体操作</li>
<li><strong>数据粒度</strong>：行级别，由后端控制</li>
</ul>
<h3 id="_2-权限缓存" tabindex="-1">2. 权限缓存 <a class="header-anchor" href="#_2-权限缓存" aria-label="Permalink to &quot;2. 权限缓存&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 登录后缓存权限，避免重复请求</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 权限存储在 Pinia 中</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setPermissions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(permissions);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 需要时从 store 获取</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> permissions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getPermissions;</span></span></code></pre>
</div><h3 id="_3-权限刷新" tabindex="-1">3. 权限刷新 <a class="header-anchor" href="#_3-权限刷新" aria-label="Permalink to &quot;3. 权限刷新&quot;">&ZeroWidthSpace;</a></h3>
<p>用户权限变更后需要刷新：</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 方式一：重新登录</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">logout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">router.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/login'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 方式二：重新获取权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUserInfoAction</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 刷新页面以重新生成路由</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">window.location.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reload</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span></code></pre>
</div><h3 id="_4-超级管理员" tabindex="-1">4. 超级管理员 <a class="header-anchor" href="#_4-超级管理员" aria-label="Permalink to &quot;4. 超级管理员&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 超级管理员拥有所有权限</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permissions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'*:*:*'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/backend/">后端开发</a> - 学习后端权限控制</li>
<li><a href="/zh/guide/packages/data_permission.html">数据权限</a> - 了解数据级权限控制</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[介绍 ]]></title>
            <link>https://docs.battcn.com/zh/guide/</link>
            <guid>https://docs.battcn.com/zh/guide/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">使命</p>
<p><strong>打破假开源，做全网最优秀、最简单、最漂亮的开源 SAAS、多租户云平台架构。从开源中来，到开源中去，让大家有更多时间<code>摸鱼</code>、<code>学习</code>、<code>陪伴家人</code>、<code>锻炼身体</code>、<code>找对象</code></strong></p>
</div>
<h2 id="项目简介" tabindex="-1">项目简介 <a class="header-anchor" href="#项目简介" aria-label="Permalink to &quot;项目简介&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>Wemirr Platform</strong> 是一套基于 Spring Cloud Alibaba 2023 的微服务多租户 SaaS 平台，提供完整的企业级解决方案。</p>
<table tabindex="0">
<thead>
<tr>
<th>项目</th>
<th>说明</th>
<th>地址</th>
</tr>
</thead>
<tbody>
<tr>
<td>wemirr-platform</td>
<td>后端服务</td>
<td><a href="https://gitee.com/battcn/wemirr-platform" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform</a></td>
</tr>
<tr>
<td>wemirr-platform-ui</td>
<td>前端项目</td>
<td><a href="https://gitee.com/battcn/wemirr-platform-ui" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform-ui</a></td>
</tr>
<tr>
<td>在线文档</td>
<td>开发文档</td>
<td><a href="https://docs.battcn.com" target="_blank" rel="noreferrer">https://docs.battcn.com</a></td>
</tr>
<tr>
<td>演示地址</td>
<td>在线体验</td>
<td><a href="https://cloud.battcn.com" target="_blank" rel="noreferrer">https://cloud.battcn.com</a></td>
</tr>
</tbody>
</table>
<h3 id="演示账号" tabindex="-1">演示账号 <a class="header-anchor" href="#演示账号" aria-label="Permalink to &quot;演示账号&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>租户</th>
<th>账号</th>
<th>密码</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>0000</td>
<td>admin</td>
<td>123456</td>
<td>平台管理员</td>
</tr>
<tr>
<td>8888</td>
<td>admin</td>
<td>123456</td>
<td>普通租户</td>
</tr>
</tbody>
</table>
<h2 id="版本说明" tabindex="-1">版本说明 <a class="header-anchor" href="#版本说明" aria-label="Permalink to &quot;版本说明&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>版本</th>
<th>JDK</th>
<th>Spring Cloud</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>v4-dev</strong></td>
<td>JDK21</td>
<td>2024</td>
<td><strong>推荐使用</strong>，后期重心在此版本</td>
</tr>
<tr>
<td>v3-dev</td>
<td>JDK17</td>
<td>2024</td>
<td>审批流基于 Camunda</td>
</tr>
<tr>
<td>v2-dev</td>
<td>JDK17</td>
<td>2023</td>
<td>仅 BUG 修复</td>
</tr>
</tbody>
</table>
<h2 id="技术架构" tabindex="-1">技术架构 <a class="header-anchor" href="#技术架构" aria-label="Permalink to &quot;技术架构&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="后端技术栈" tabindex="-1">后端技术栈 <a class="header-anchor" href="#后端技术栈" aria-label="Permalink to &quot;后端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Spring Boot</td>
<td>3.x</td>
<td>基础框架</td>
</tr>
<tr>
<td>Spring Cloud</td>
<td>2024</td>
<td>微服务框架</td>
</tr>
<tr>
<td>Spring Cloud Alibaba</td>
<td>2023</td>
<td>阿里巴巴微服务组件</td>
</tr>
<tr>
<td>Nacos</td>
<td>2.x</td>
<td>注册中心 &amp; 配置中心</td>
</tr>
<tr>
<td>Sentinel</td>
<td>1.8.x</td>
<td>流量控制 &amp; 熔断降级</td>
</tr>
<tr>
<td>Sa-Token</td>
<td>1.x</td>
<td>权限认证框架</td>
</tr>
<tr>
<td>MyBatis-Plus</td>
<td>3.5.x</td>
<td>ORM 框架</td>
</tr>
<tr>
<td>Redis</td>
<td>7.x</td>
<td>缓存 &amp; 分布式锁</td>
</tr>
<tr>
<td>MySQL</td>
<td>8.x</td>
<td>数据库</td>
</tr>
<tr>
<td>Warm-Flow</td>
<td>最新版</td>
<td>审批流引擎</td>
</tr>
<tr>
<td>Snail-Job</td>
<td>最新版</td>
<td>分布式任务调度</td>
</tr>
<tr>
<td>Langchain4j</td>
<td>最新版</td>
<td>AI 能力集成</td>
</tr>
</tbody>
</table>
<h3 id="前端技术栈" tabindex="-1">前端技术栈 <a class="header-anchor" href="#前端技术栈" aria-label="Permalink to &quot;前端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vue</td>
<td>3.x</td>
<td>渐进式 JavaScript 框架</td>
</tr>
<tr>
<td>Vben Admin</td>
<td>5.x</td>
<td>后台管理模板（Monorepo）</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.x</td>
<td>UI 组件库</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.x</td>
<td>类型安全</td>
</tr>
<tr>
<td>Vite</td>
<td>5.x</td>
<td>构建工具</td>
</tr>
<tr>
<td>Pinia</td>
<td>2.x</td>
<td>状态管理</td>
</tr>
<tr>
<td>TailwindCSS</td>
<td>3.x</td>
<td>原子化 CSS</td>
</tr>
</tbody>
</table>
<h2 id="平台特点" tabindex="-1">平台特点 <a class="header-anchor" href="#平台特点" aria-label="Permalink to &quot;平台特点&quot;">&ZeroWidthSpace;</a></h2>
<div class="info custom-block"><p class="custom-block-title">🚀 核心能力</p>
<blockquote>
<ul>
<li><strong>多租户架构</strong> — 字段隔离 / Schema 隔离 / 数据源隔离，按需选择</li>
<li><strong>RBAC + 数据权限</strong> — 按钮级功能权限 + 全部/本级/本级及子级/仅本人/自定义</li>
<li><strong>动态网关</strong> — Redis / Nacos 配置，限流熔断，黑白名单</li>
<li><strong>分布式消息</strong> — WebSocket + Redis 集群消息推送</li>
<li><strong>链路追踪</strong> — SkyWalking / Zipkin / Pinpoint 无缝对接</li>
<li><strong>AI 集成</strong> — Langchain4j 驱动，RAG / Tools / MCP 开箱即用</li>
</ul>
</blockquote>
</div>
<div class="tip custom-block"><p class="custom-block-title">🎯 技术亮点</p>
<blockquote>
<ul>
<li><strong>插拔设计</strong> — 可选组件在 <code>wemirr-plugin</code>，按需引入零耦合</li>
<li><strong>极简代码</strong> — 注解驱动，告别模板代码，开发效率翻倍</li>
<li><strong>高性能</strong> — 接口 P99 响应 10-150ms，虚拟线程 + 连接池优化</li>
<li><strong>标准文档</strong> — SpringDoc（Swagger V3）自动生成，在线调试</li>
</ul>
</blockquote>
</div>
<hr>
<h2 id="内置功能" tabindex="-1">内置功能 <a class="header-anchor" href="#内置功能" aria-label="Permalink to &quot;内置功能&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>分类</th>
<th>功能模块</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>🔐 系统管理</strong></td>
<td>用户管理 · 角色管理 · 菜单管理 · 部门管理 · 岗位管理 · 字典管理</td>
</tr>
<tr>
<td><strong>🏢 平台管理</strong></td>
<td>租户管理 · 产品套餐 · 数据源管理 · 租户配置 <em>(超管专属)</em></td>
</tr>
<tr>
<td><strong>📊 系统监控</strong></td>
<td>操作日志 · 登录日志 · 在线用户 · 服务监控 · 缓存监控</td>
</tr>
<tr>
<td><strong>🛠️ 开发工具</strong></td>
<td>代码生成 · 系统接口 · 网关管理 · 限流规则</td>
</tr>
<tr>
<td><strong>📦 业务插件</strong></td>
<td><code>workflow</code> 审批流 · <code>ai</code> 智能助手 · <code>monitor</code> 监控 · <code>wms</code> 仓储 · <code>tms</code> 运输</td>
</tr>
</tbody>
</table>
<h2 id="快速体验" tabindex="-1">快速体验 <a class="header-anchor" href="#快速体验" aria-label="Permalink to &quot;快速体验&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆后端代码</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆前端代码</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div><p>详细部署步骤请参考 <a href="/zh/guide/quickstart/">快速上手</a></p>
<h2 id="交流群" tabindex="-1">交流群 <a class="header-anchor" href="#交流群" aria-label="Permalink to &quot;交流群&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><strong>技术交流 QQ 群</strong>：789517089</li>
<li><strong>作者微信</strong>：battcn2022</li>
</ul>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/guide/quickstart/">快速上手</a> - 5分钟启动项目</li>
<li><a href="/zh/guide/directory.html">项目结构</a> - 了解项目目录结构</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 容器化部署</li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">使命</p>
<p><strong>打破假开源，做全网最优秀、最简单、最漂亮的开源 SAAS、多租户云平台架构。从开源中来，到开源中去，让大家有更多时间<code>摸鱼</code>、<code>学习</code>、<code>陪伴家人</code>、<code>锻炼身体</code>、<code>找对象</code></strong></p>
</div>
<h2 id="项目简介" tabindex="-1">项目简介 <a class="header-anchor" href="#项目简介" aria-label="Permalink to &quot;项目简介&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>Wemirr Platform</strong> 是一套基于 Spring Cloud Alibaba 2023 的微服务多租户 SaaS 平台，提供完整的企业级解决方案。</p>
<table tabindex="0">
<thead>
<tr>
<th>项目</th>
<th>说明</th>
<th>地址</th>
</tr>
</thead>
<tbody>
<tr>
<td>wemirr-platform</td>
<td>后端服务</td>
<td><a href="https://gitee.com/battcn/wemirr-platform" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform</a></td>
</tr>
<tr>
<td>wemirr-platform-ui</td>
<td>前端项目</td>
<td><a href="https://gitee.com/battcn/wemirr-platform-ui" target="_blank" rel="noreferrer">https://gitee.com/battcn/wemirr-platform-ui</a></td>
</tr>
<tr>
<td>在线文档</td>
<td>开发文档</td>
<td><a href="https://docs.battcn.com" target="_blank" rel="noreferrer">https://docs.battcn.com</a></td>
</tr>
<tr>
<td>演示地址</td>
<td>在线体验</td>
<td><a href="https://cloud.battcn.com" target="_blank" rel="noreferrer">https://cloud.battcn.com</a></td>
</tr>
</tbody>
</table>
<h3 id="演示账号" tabindex="-1">演示账号 <a class="header-anchor" href="#演示账号" aria-label="Permalink to &quot;演示账号&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>租户</th>
<th>账号</th>
<th>密码</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>0000</td>
<td>admin</td>
<td>123456</td>
<td>平台管理员</td>
</tr>
<tr>
<td>8888</td>
<td>admin</td>
<td>123456</td>
<td>普通租户</td>
</tr>
</tbody>
</table>
<h2 id="版本说明" tabindex="-1">版本说明 <a class="header-anchor" href="#版本说明" aria-label="Permalink to &quot;版本说明&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>版本</th>
<th>JDK</th>
<th>Spring Cloud</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>v4-dev</strong></td>
<td>JDK21</td>
<td>2024</td>
<td><strong>推荐使用</strong>，后期重心在此版本</td>
</tr>
<tr>
<td>v3-dev</td>
<td>JDK17</td>
<td>2024</td>
<td>审批流基于 Camunda</td>
</tr>
<tr>
<td>v2-dev</td>
<td>JDK17</td>
<td>2023</td>
<td>仅 BUG 修复</td>
</tr>
</tbody>
</table>
<h2 id="技术架构" tabindex="-1">技术架构 <a class="header-anchor" href="#技术架构" aria-label="Permalink to &quot;技术架构&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="后端技术栈" tabindex="-1">后端技术栈 <a class="header-anchor" href="#后端技术栈" aria-label="Permalink to &quot;后端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Spring Boot</td>
<td>3.x</td>
<td>基础框架</td>
</tr>
<tr>
<td>Spring Cloud</td>
<td>2024</td>
<td>微服务框架</td>
</tr>
<tr>
<td>Spring Cloud Alibaba</td>
<td>2023</td>
<td>阿里巴巴微服务组件</td>
</tr>
<tr>
<td>Nacos</td>
<td>2.x</td>
<td>注册中心 &amp; 配置中心</td>
</tr>
<tr>
<td>Sentinel</td>
<td>1.8.x</td>
<td>流量控制 &amp; 熔断降级</td>
</tr>
<tr>
<td>Sa-Token</td>
<td>1.x</td>
<td>权限认证框架</td>
</tr>
<tr>
<td>MyBatis-Plus</td>
<td>3.5.x</td>
<td>ORM 框架</td>
</tr>
<tr>
<td>Redis</td>
<td>7.x</td>
<td>缓存 &amp; 分布式锁</td>
</tr>
<tr>
<td>MySQL</td>
<td>8.x</td>
<td>数据库</td>
</tr>
<tr>
<td>Warm-Flow</td>
<td>最新版</td>
<td>审批流引擎</td>
</tr>
<tr>
<td>Snail-Job</td>
<td>最新版</td>
<td>分布式任务调度</td>
</tr>
<tr>
<td>Langchain4j</td>
<td>最新版</td>
<td>AI 能力集成</td>
</tr>
</tbody>
</table>
<h3 id="前端技术栈" tabindex="-1">前端技术栈 <a class="header-anchor" href="#前端技术栈" aria-label="Permalink to &quot;前端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vue</td>
<td>3.x</td>
<td>渐进式 JavaScript 框架</td>
</tr>
<tr>
<td>Vben Admin</td>
<td>5.x</td>
<td>后台管理模板（Monorepo）</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.x</td>
<td>UI 组件库</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.x</td>
<td>类型安全</td>
</tr>
<tr>
<td>Vite</td>
<td>5.x</td>
<td>构建工具</td>
</tr>
<tr>
<td>Pinia</td>
<td>2.x</td>
<td>状态管理</td>
</tr>
<tr>
<td>TailwindCSS</td>
<td>3.x</td>
<td>原子化 CSS</td>
</tr>
</tbody>
</table>
<h2 id="平台特点" tabindex="-1">平台特点 <a class="header-anchor" href="#平台特点" aria-label="Permalink to &quot;平台特点&quot;">&ZeroWidthSpace;</a></h2>
<div class="info custom-block"><p class="custom-block-title">🚀 核心能力</p>
<blockquote>
<ul>
<li><strong>多租户架构</strong> — 字段隔离 / Schema 隔离 / 数据源隔离，按需选择</li>
<li><strong>RBAC + 数据权限</strong> — 按钮级功能权限 + 全部/本级/本级及子级/仅本人/自定义</li>
<li><strong>动态网关</strong> — Redis / Nacos 配置，限流熔断，黑白名单</li>
<li><strong>分布式消息</strong> — WebSocket + Redis 集群消息推送</li>
<li><strong>链路追踪</strong> — SkyWalking / Zipkin / Pinpoint 无缝对接</li>
<li><strong>AI 集成</strong> — Langchain4j 驱动，RAG / Tools / MCP 开箱即用</li>
</ul>
</blockquote>
</div>
<div class="tip custom-block"><p class="custom-block-title">🎯 技术亮点</p>
<blockquote>
<ul>
<li><strong>插拔设计</strong> — 可选组件在 <code>wemirr-plugin</code>，按需引入零耦合</li>
<li><strong>极简代码</strong> — 注解驱动，告别模板代码，开发效率翻倍</li>
<li><strong>高性能</strong> — 接口 P99 响应 10-150ms，虚拟线程 + 连接池优化</li>
<li><strong>标准文档</strong> — SpringDoc（Swagger V3）自动生成，在线调试</li>
</ul>
</blockquote>
</div>
<hr>
<h2 id="内置功能" tabindex="-1">内置功能 <a class="header-anchor" href="#内置功能" aria-label="Permalink to &quot;内置功能&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>分类</th>
<th>功能模块</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>🔐 系统管理</strong></td>
<td>用户管理 · 角色管理 · 菜单管理 · 部门管理 · 岗位管理 · 字典管理</td>
</tr>
<tr>
<td><strong>🏢 平台管理</strong></td>
<td>租户管理 · 产品套餐 · 数据源管理 · 租户配置 <em>(超管专属)</em></td>
</tr>
<tr>
<td><strong>📊 系统监控</strong></td>
<td>操作日志 · 登录日志 · 在线用户 · 服务监控 · 缓存监控</td>
</tr>
<tr>
<td><strong>🛠️ 开发工具</strong></td>
<td>代码生成 · 系统接口 · 网关管理 · 限流规则</td>
</tr>
<tr>
<td><strong>📦 业务插件</strong></td>
<td><code>workflow</code> 审批流 · <code>ai</code> 智能助手 · <code>monitor</code> 监控 · <code>wms</code> 仓储 · <code>tms</code> 运输</td>
</tr>
</tbody>
</table>
<h2 id="快速体验" tabindex="-1">快速体验 <a class="header-anchor" href="#快速体验" aria-label="Permalink to &quot;快速体验&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆后端代码</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆前端代码</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div><p>详细部署步骤请参考 <a href="/zh/guide/quickstart/">快速上手</a></p>
<h2 id="交流群" tabindex="-1">交流群 <a class="header-anchor" href="#交流群" aria-label="Permalink to &quot;交流群&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><strong>技术交流 QQ 群</strong>：789517089</li>
<li><strong>作者微信</strong>：battcn2022</li>
</ul>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/guide/quickstart/">快速上手</a> - 5分钟启动项目</li>
<li><a href="/zh/guide/directory.html">项目结构</a> - 了解项目目录结构</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 容器化部署</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[数据权限 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/data_permission.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/data_permission.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="数据权限" tabindex="-1">数据权限 <a class="header-anchor" href="#数据权限" aria-label="Permalink to &quot;数据权限&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">多维度数据权限</p>
<p>为保障不同决策身份的用户数据安全，会将划分每个用户可见的数据范围</p>
<ul>
<li>全部：可以看所有的数据（常为管理员角色）</li>
<li>本级及子级：常用语组织架构领导层</li>
<li>本级：用处较少，常用部门组长之类的</li>
<li>自定义：可以根据自己业务场景自定义权限范围和资源</li>
<li>个人：顾名思义，只能看自己创建的数据</li>
</ul>
<p><code>wemirr-platform</code> 只需进行少量的规则代码配置，即可实现灵活多维度的数据权限控制。</p>
</div>
<h2 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="手动使用" tabindex="-1">手动使用 <a class="header-anchor" href="#手动使用" aria-label="Permalink to &quot;手动使用&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>手动API调用,通过 <code>DataPermissionUtils.execute()</code></strong> 方式构建 <code>DataPermissionRule</code> 即可</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">in</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ParameterIn.QUERY),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "principal"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">in</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ParameterIn.QUERY)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "查询日志 - [DONE] - [Levin]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "查询日志 - [DONE] - [Levin]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PreAuthorize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hasAuthority('log:login:page')"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Page</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PageRequest request, String name, String principal) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeDefaultDataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            loginLogService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">like</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, name)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">like</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getPrincipal, principal).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orderByDesc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCreatedTime)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="注解使用" tabindex="-1">注解使用 <a class="header-anchor" href="#注解使用" aria-label="Permalink to &quot;注解使用&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>可以指定多个字段多个维度的数据权限,可以自行灵活定义</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Entity.CREATE_USER_COLUMN,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "company_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.COMPANY),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> findPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"page"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> page, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) UserPageReq req);</span></span></code></pre>
</div><h2 id="核心实现" tabindex="-1">核心实现 <a class="header-anchor" href="#核心实现" aria-label="Permalink to &quot;核心实现&quot;">&ZeroWidthSpace;</a></h2>
<p>基于 MP 提供的 <code>MultiDataPermissionHandler</code> 实现了动态多维度数据权限</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopePermissionHandler</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MultiDataPermissionHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SneakyThrows</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getSqlSegment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Expression </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 匿名用户不进入数据权限插件</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">anonymous</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isDebugEnabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sql statementId{},where - {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, mappedStatementId, where);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 默认从当前线程上下文获取,兼容  DataPermissionUtils.executeWithDataPermissionRule 方式</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRuleHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">peek</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 注解的优先级最低</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionRuleByMappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mappedStatementId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(table, rule);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, DataPermissionRule </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.ALL </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isIgnore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> columns </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getColumns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Expression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> conditions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildConditions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(context, table, columns);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (CollUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(conditions)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 使用循环将条件逐个与前面的条件组合起来</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> conditions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reduce</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AndExpression</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="数据权限" tabindex="-1">数据权限 <a class="header-anchor" href="#数据权限" aria-label="Permalink to &quot;数据权限&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">多维度数据权限</p>
<p>为保障不同决策身份的用户数据安全，会将划分每个用户可见的数据范围</p>
<ul>
<li>全部：可以看所有的数据（常为管理员角色）</li>
<li>本级及子级：常用语组织架构领导层</li>
<li>本级：用处较少，常用部门组长之类的</li>
<li>自定义：可以根据自己业务场景自定义权限范围和资源</li>
<li>个人：顾名思义，只能看自己创建的数据</li>
</ul>
<p><code>wemirr-platform</code> 只需进行少量的规则代码配置，即可实现灵活多维度的数据权限控制。</p>
</div>
<h2 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="手动使用" tabindex="-1">手动使用 <a class="header-anchor" href="#手动使用" aria-label="Permalink to &quot;手动使用&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>手动API调用,通过 <code>DataPermissionUtils.execute()</code></strong> 方式构建 <code>DataPermissionRule</code> 即可</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">in</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ParameterIn.QUERY),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "principal"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "账号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">in</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ParameterIn.QUERY)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "查询日志 - [DONE] - [Levin]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "查询日志 - [DONE] - [Levin]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PreAuthorize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"hasAuthority('log:login:page')"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Page</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PageRequest request, String name, String principal) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeDefaultDataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            loginLogService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">like</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, name)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">like</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getPrincipal, principal).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orderByDesc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoginLog</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCreatedTime)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="注解使用" tabindex="-1">注解使用 <a class="header-anchor" href="#注解使用" aria-label="Permalink to &quot;注解使用&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>可以指定多个字段多个维度的数据权限,可以自行灵活定义</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Entity.CREATE_USER_COLUMN,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "company_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.COMPANY),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> findPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"page"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) IPage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> page, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) UserPageReq req);</span></span></code></pre>
</div><h2 id="核心实现" tabindex="-1">核心实现 <a class="header-anchor" href="#核心实现" aria-label="Permalink to &quot;核心实现&quot;">&ZeroWidthSpace;</a></h2>
<p>基于 MP 提供的 <code>MultiDataPermissionHandler</code> 实现了动态多维度数据权限</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopePermissionHandler</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MultiDataPermissionHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SneakyThrows</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getSqlSegment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Expression </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 匿名用户不进入数据权限插件</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">anonymous</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isDebugEnabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sql statementId{},where - {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, mappedStatementId, where);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 默认从当前线程上下文获取,兼容  DataPermissionUtils.executeWithDataPermissionRule 方式</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRuleHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">peek</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 注解的优先级最低</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionRuleByMappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mappedStatementId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(table, rule);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, DataPermissionRule </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.ALL </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isIgnore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> columns </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getColumns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Expression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> conditions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildConditions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(context, table, columns);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (CollUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(conditions)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 使用循环将条件逐个与前面的条件组合起来</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> conditions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reduce</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AndExpression</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Docker Compose ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/devops/docker-compose.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/devops/docker-compose.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="docker-compose" tabindex="-1">Docker Compose <a class="header-anchor" href="#docker-compose" aria-label="Permalink to &quot;Docker Compose&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">提供一件安装开发环境的 Docker 脚本</p>
<p>如果本地有 <code>Docker</code> 环境可以直接用下面的方式快速安装开发环境</p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">version:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2'</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果执行失败,请选尝试创建 docker network create wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">networks:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  default:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    driver:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bridge</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    external:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">services:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-mysql:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 建议安装最新的</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0.19</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      MYSQL_ROOT_PASSWORD:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 123456</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      MYSQL_ROOT_HOST:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '%'</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      TZ:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-mysql</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    command</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --character-set-server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">utf8mb4</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --collation-server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">utf8mb4_general_ci</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --explicit_defaults_for_timestamp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --lower_case_table_names</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --max_allowed_packet</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">128M</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --default-authentication-plugin</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">caching_sha2_password</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-redis:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:5.0</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-redis</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-nacos:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-nacos</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server:2.0.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      MODE:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      JVM_XMS:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 512m</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      JVM_MMS:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 320m</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9849:9849</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-rabbitmq:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-rabbitmq</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker.io/macintoshplus/rabbitmq-management</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #安装 XXL-JOB-ADMIN(如果数据库也是docker 运行需要配置统一网络 例如： docker network create wemirr )</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #docker pull xuxueli/xxl-job-admin:2.3.0</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #docker run -e PARAMS="--spring.datasource.username=root --spring.datasource.password=123456 --spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver --spring.datasource.url=jdbc:mysql://127.0.0.1:3306/wemirr-platform?useUnicode=true&#x26;characterEncoding=UTF-8&#x26;autoReconnect=true" -p 9999:8080 -v /Users/battcn/Development:/data/applogs --name xxl-job-admin  -d xuxueli/xxl-job-admin:2.3.0</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-elasticsearch:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      discovery.type:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> single-node</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9200:9200</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9300:9300</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-elasticsearch</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-oap:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-oap</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      SW_STORAGE:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      SW_STORAGE_ES_CLUSTER_NODES:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-elasticsearch:9200</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 1234:1234</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 11800:11800</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 12800:12800</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-oap-ui:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-oap-ui</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      SW_OAP_ADDRESS:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-oap:12800</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      TZ:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10086:8080</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="docker-compose" tabindex="-1">Docker Compose <a class="header-anchor" href="#docker-compose" aria-label="Permalink to &quot;Docker Compose&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">提供一件安装开发环境的 Docker 脚本</p>
<p>如果本地有 <code>Docker</code> 环境可以直接用下面的方式快速安装开发环境</p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">version:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '2'</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 如果执行失败,请选尝试创建 docker network create wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">networks:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  default:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    driver:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bridge</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    external:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">services:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-mysql:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 建议安装最新的</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:8.0.19</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      MYSQL_ROOT_PASSWORD:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 123456</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      MYSQL_ROOT_HOST:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '%'</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      TZ:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-mysql</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    command</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --character-set-server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">utf8mb4</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --collation-server</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">utf8mb4_general_ci</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --explicit_defaults_for_timestamp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --lower_case_table_names</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">1</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --max_allowed_packet</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">128M</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      --default-authentication-plugin</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">caching_sha2_password</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-redis:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:5.0</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-redis</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-nacos:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-nacos</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server:2.0.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      MODE:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> standalone</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      JVM_XMS:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 512m</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      JVM_MMS:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 320m</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9849:9849</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-rabbitmq:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-rabbitmq</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> docker.io/macintoshplus/rabbitmq-management</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5671:5671</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5672:5672</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 15672:15672</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 25672:25672</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #安装 XXL-JOB-ADMIN(如果数据库也是docker 运行需要配置统一网络 例如： docker network create wemirr )</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #docker pull xuxueli/xxl-job-admin:2.3.0</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #docker run -e PARAMS="--spring.datasource.username=root --spring.datasource.password=123456 --spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver --spring.datasource.url=jdbc:mysql://127.0.0.1:3306/wemirr-platform?useUnicode=true&#x26;characterEncoding=UTF-8&#x26;autoReconnect=true" -p 9999:8080 -v /Users/battcn/Development:/data/applogs --name xxl-job-admin  -d xuxueli/xxl-job-admin:2.3.0</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-elasticsearch:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch:7.9.3</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      discovery.type:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> single-node</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9200:9200</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9300:9300</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-elasticsearch</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-oap:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-oap</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-oap-server:8.5.0-es7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      SW_STORAGE:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> elasticsearch7</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      SW_STORAGE_ES_CLUSTER_NODES:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-elasticsearch:9200</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 1234:1234</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 11800:11800</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 12800:12800</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  wemirr-platform-oap-ui:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    container_name:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-oap-ui</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apache/skywalking-ui:8.5.0</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      SW_OAP_ADDRESS:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-oap:12800</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      TZ:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Asia/Shanghai</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ports:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      -</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10086:8080</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    restart:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> always</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[运维管理 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/devops/</link>
            <guid>https://docs.battcn.com/zh/guide/packages/devops/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="运维管理" tabindex="-1">运维管理 <a class="header-anchor" href="#运维管理" aria-label="Permalink to &quot;运维管理&quot;">&ZeroWidthSpace;</a></h1>
]]></description>
            <content:encoded><![CDATA[<h1 id="运维管理" tabindex="-1">运维管理 <a class="header-anchor" href="#运维管理" aria-label="Permalink to &quot;运维管理&quot;">&ZeroWidthSpace;</a></h1>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[监控中心 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/devops/monitor.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/devops/monitor.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="监控中心" tabindex="-1">监控中心 <a class="header-anchor" href="#监控中心" aria-label="Permalink to &quot;监控中心&quot;">&ZeroWidthSpace;</a></h1>
]]></description>
            <content:encoded><![CDATA[<h1 id="监控中心" tabindex="-1">监控中心 <a class="header-anchor" href="#监控中心" aria-label="Permalink to &quot;监控中心&quot;">&ZeroWidthSpace;</a></h1>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nginx 配置 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/devops/nginx.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/devops/nginx.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="nginx-配置" tabindex="-1">Nginx 配置 <a class="header-anchor" href="#nginx-配置" aria-label="Permalink to &quot;Nginx 配置&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="请求头转发" tabindex="-1">请求头转发 <a class="header-anchor" href="#请求头转发" aria-label="Permalink to &quot;请求头转发&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p><strong>不配置转发自定义请求头的话租户编码不会转发</strong></p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">http</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">       mime.types</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    default_type</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  application/octet-stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 默认应该是 off , 所以要打开 </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    underscores_in_headers</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="http-配置" tabindex="-1">HTTP 配置 <a class="header-anchor" href="#http-配置" aria-label="Permalink to &quot;HTTP 配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">server</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    server_name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    root</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr-platform/cloud-ui/dist</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        gzip_static</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /api/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_pass</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost:9000/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_redirect</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Host</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $host;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Real-IP</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Forwarded-For</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_http_version</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Upgrade</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Connection</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_max_body_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_body_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_connect_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_send_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_read_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffers</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_busy_buffers_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_temp_file_write_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="https-配置" tabindex="-1">HTTPS 配置 <a class="header-anchor" href="#https-配置" aria-label="Permalink to &quot;HTTPS 配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">server</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">       80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    server_name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 强制跳转 HTTPS </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        rewrite</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ^</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)$  </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">server</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 443</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ssl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    server_name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 证书是从阿里云申请的免费证书</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_certificate</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">   xxxx.battcn.com.pem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_certificate_key</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  xxxx.battcn.com.key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_session_timeout</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_ciphers</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_protocols</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TLSv1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TLSv1.1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TLSv1.2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_prefer_server_ciphers</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    root</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr-platform/cloud-ui/dist</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        gzip_static</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /api/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_pass</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost:9000/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_redirect</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Host</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $host;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Real-IP</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Forwarded-For</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $proxy_add_x_forwarded_for;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_http_version</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Upgrade</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Connection</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_max_body_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_body_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_connect_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_send_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_read_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffers</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_busy_buffers_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_temp_file_write_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="nginx-配置" tabindex="-1">Nginx 配置 <a class="header-anchor" href="#nginx-配置" aria-label="Permalink to &quot;Nginx 配置&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="请求头转发" tabindex="-1">请求头转发 <a class="header-anchor" href="#请求头转发" aria-label="Permalink to &quot;请求头转发&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p><strong>不配置转发自定义请求头的话租户编码不会转发</strong></p>
</div>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">http</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    include</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">       mime.types</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    default_type</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  application/octet-stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 默认应该是 off , 所以要打开 </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    underscores_in_headers</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="http-配置" tabindex="-1">HTTP 配置 <a class="header-anchor" href="#http-配置" aria-label="Permalink to &quot;HTTP 配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">server</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    server_name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    root</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr-platform/cloud-ui/dist</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        gzip_static</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /api/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_pass</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost:9000/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_redirect</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Host</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $host;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Real-IP</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Forwarded-For</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $proxy_add_x_forwarded_for;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_http_version</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Upgrade</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Connection</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_max_body_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_body_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_connect_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_send_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_read_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffers</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_busy_buffers_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_temp_file_write_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="https-配置" tabindex="-1">HTTPS 配置 <a class="header-anchor" href="#https-配置" aria-label="Permalink to &quot;HTTPS 配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">server</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">       80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    server_name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 强制跳转 HTTPS </span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        rewrite</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ^</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)$  </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">server</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 443</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ssl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    server_name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  cloud.battcn.com</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 证书是从阿里云申请的免费证书</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_certificate</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">   xxxx.battcn.com.pem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_certificate_key</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  xxxx.battcn.com.key</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_session_timeout</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 5m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_ciphers</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_protocols</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TLSv1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TLSv1.1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TLSv1.2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ssl_prefer_server_ciphers</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # vue 静态资源打成tar.gz 包 开启压缩 速度更快</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    root</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /opt/wemirr-platform/cloud-ui/dist</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        gzip_static</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    location</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /api/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_pass</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> http://localhost:9000/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_redirect</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> off</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Host</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $host;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Real-IP</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $remote_addr;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> X-Forwarded-For</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $proxy_add_x_forwarded_for;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_http_version</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Upgrade</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $http_upgrade;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_set_header</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> Connection</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "upgrade"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_max_body_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 10m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        client_body_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 128k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_connect_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_send_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_read_timeout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 90</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffer_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 4k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_buffers</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 4</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 32k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_busy_buffers_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        proxy_temp_file_write_size</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 64k</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[流量保护 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/devops/sentinel.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/devops/sentinel.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="流量保护" tabindex="-1">流量保护 <a class="header-anchor" href="#流量保护" aria-label="Permalink to &quot;流量保护&quot;">&ZeroWidthSpace;</a></h1>
]]></description>
            <content:encoded><![CDATA[<h1 id="流量保护" tabindex="-1">流量保护 <a class="header-anchor" href="#流量保护" aria-label="Permalink to &quot;流量保护&quot;">&ZeroWidthSpace;</a></h1>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Excel 用法 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/excel.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/excel.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="excel-用法" tabindex="-1">Excel 用法 <a class="header-anchor" href="#excel-用法" aria-label="Permalink to &quot;Excel 用法&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>添加依赖</p>
</blockquote>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>easyexcel-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="导出" tabindex="-1">导出 <a class="header-anchor" href="#导出" aria-label="Permalink to &quot;导出&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="方法一-build-api" tabindex="-1">方法一：Build API <a class="header-anchor" href="#方法一-build-api" aria-label="Permalink to &quot;方法一：Build API&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>根据实际业务需要,自己设置写入的文件和Sheet 内容,方便动态赋值</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExcelWriteFile </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">exportList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserPageReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 可以根据业务需要自己构建导出对象</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExcelWriteFile.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fileName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"文件名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="方法二-注解" tabindex="-1">方法二：注解 <a class="header-anchor" href="#方法二-注解" aria-label="Permalink to &quot;方法二：注解&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>注解导出，省时省力，缺点就是不能根据条件动态去设置文件和Sheet</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fileName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> exportList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 直接返回需要导出的集合即可</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="导入" tabindex="-1">导入 <a class="header-anchor" href="#导入" aria-label="Permalink to &quot;导入&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>根据实际业务需要,自己设置的文件和Sheet 内容,方便动态赋值（如：密码、SheetNo、SheetName）</strong></p>
<h3 id="方法一-build-api-1" tabindex="-1">方法一：Build API <a class="header-anchor" href="#方法一-build-api-1" aria-label="Permalink to &quot;方法一：Build API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SneakyThrows</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaIgnore</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/import1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "普通导入 - 1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ValidateAnalysisEventListener</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;?></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> import1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultipartFile file) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExcelReadResolver.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">read</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ExcelReadFile.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">inputStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(file.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getInputStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="方法二-注解-1" tabindex="-1">方法二：注解 <a class="header-anchor" href="#方法二-注解-1" aria-label="Permalink to &quot;方法二：注解&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>注解导入，省时省力，缺点就是不能根据条件动态去设置文件和Sheet</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaIgnore</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/import2"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "普通导入 - 2"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "file"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ExcelDomain</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> import2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ExcelDomain</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="excel-用法" tabindex="-1">Excel 用法 <a class="header-anchor" href="#excel-用法" aria-label="Permalink to &quot;Excel 用法&quot;">&ZeroWidthSpace;</a></h1>
<blockquote>
<p>添加依赖</p>
</blockquote>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>easyexcel-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="导出" tabindex="-1">导出 <a class="header-anchor" href="#导出" aria-label="Permalink to &quot;导出&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="方法一-build-api" tabindex="-1">方法一：Build API <a class="header-anchor" href="#方法一-build-api" aria-label="Permalink to &quot;方法一：Build API&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>根据实际业务需要,自己设置写入的文件和Sheet 内容,方便动态赋值</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExcelWriteFile </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">exportList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserPageReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 可以根据业务需要自己构建导出对象</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExcelWriteFile.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fileName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"文件名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">data</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="方法二-注解" tabindex="-1">方法二：注解 <a class="header-anchor" href="#方法二-注解" aria-label="Permalink to &quot;方法二：注解&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>注解导出，省时省力，缺点就是不能根据条件动态去设置文件和Sheet</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fileName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> exportList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 直接返回需要导出的集合即可</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="导入" tabindex="-1">导入 <a class="header-anchor" href="#导入" aria-label="Permalink to &quot;导入&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>根据实际业务需要,自己设置的文件和Sheet 内容,方便动态赋值（如：密码、SheetNo、SheetName）</strong></p>
<h3 id="方法一-build-api-1" tabindex="-1">方法一：Build API <a class="header-anchor" href="#方法一-build-api-1" aria-label="Permalink to &quot;方法一：Build API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SneakyThrows</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaIgnore</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/import1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "普通导入 - 1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ValidateAnalysisEventListener</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;?></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> import1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestParam</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultipartFile file) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ExcelReadResolver.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">read</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ExcelReadFile.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">inputStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(file.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getInputStream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="方法二-注解-1" tabindex="-1">方法二：注解 <a class="header-anchor" href="#方法二-注解-1" aria-label="Permalink to &quot;方法二：注解&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>注解导入，省时省力，缺点就是不能根据条件动态去设置文件和Sheet</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaIgnore</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/import2"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "普通导入 - 2"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Parameter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "file"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ExcelDomain</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> import2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ExcelDomain</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[无题]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/ext.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/ext.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<p>#asdasd</p>
]]></description>
            <content:encoded><![CDATA[<p>#asdasd</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Feign 服务调用 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/feign.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/feign.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="feign-服务调用" tabindex="-1">Feign 服务调用 <a class="header-anchor" href="#feign-服务调用" aria-label="Permalink to &quot;Feign 服务调用&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">使用微服务必不可少会用到 <code>Feign</code>。 为了方便大家无感知使用请认真熟读本章节</p>
<p><code>Spring Cloud OpenFeign</code> 是Spring 官方为替代进入停更维护状态的 Feign 推出的另一款声明式服务调用与负载均衡组件。</p>
<p><strong>它具有 <code>Feign</code> 的所有功能，并在其基础上增加了对 <code>Spring MVC</code> 注解的支持。</strong></p>
</div>
<h2 id="基础知识" tabindex="-1">基础知识 <a class="header-anchor" href="#基础知识" aria-label="Permalink to &quot;基础知识&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>添加下面依赖即可快速使用 <code>Spring Cloud OpenFeign</code> 与负载均衡</strong></p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-openfeign&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-loadbalancer&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="负载均衡" tabindex="-1">负载均衡 <a class="header-anchor" href="#负载均衡" aria-label="Permalink to &quot;负载均衡&quot;">&ZeroWidthSpace;</a></h3>
<p>当需要用 <code>RestTemplate</code> 显示调用的时，需要给 <code>RestTemplate</code> 的实例上添加 <code>@LoadBalanced</code> 表示它是一个负载均衡的调用</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Primary</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LoadBalanced</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RestTemplate </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbRestTemplate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RestTemplate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="性能优化" tabindex="-1">性能优化 <a class="header-anchor" href="#性能优化" aria-label="Permalink to &quot;性能优化&quot;">&ZeroWidthSpace;</a></h3>
<blockquote>
<p>Gzip 压缩</p>
</blockquote>
<div class="warning custom-block"><p class="custom-block-title">服务之间调用或者文件传输的时候开启压缩支持可以大大的提升响应效率</p>
<p>开启压缩可以有效节约网络资源，但是会增加CPU压力，建议把最小压缩的文档大小适度调大一点。</p>
</div>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  compression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    request</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 开启请求压缩</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 配置压缩支持的 MIME TYPE</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      mime-types</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">text/xml,application/xml,application/json</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 配置压缩数据大小的下限</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      min-request-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2048</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">   </span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 开启响应压缩</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><blockquote>
<p>连接池配置</p>
</blockquote>
<p>开启 <code>http-client</code> 配置替换默认的 <code>URLConnection</code> 同时配置连接池和超时时间，提高响应速度</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  httpclient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> # HttpClient的开关</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    connection-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 连接超时（毫秒）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    time-to-live</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                # 连接存活时长</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    time-to-live-unit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minutes</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     # 连接存活时长单位</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    max-connections</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">500</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 最大连接数</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    max-connections-per-route</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 单个请求路径的最大连接数</span></span></code></pre>
</div><h3 id="详细日志" tabindex="-1">详细日志 <a class="header-anchor" href="#详细日志" aria-label="Permalink to &quot;详细日志&quot;">&ZeroWidthSpace;</a></h3>
<blockquote>
<p>Bean 注入的方式</p>
</blockquote>
<p>配置如下 <code>@Bean</code> 既可开启详细日志（不过在生产环境建议关掉,可以通过表达式控制 <code>@Bean</code> 在生产环境不注入）</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Logger.Level </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignLoggerLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Logger.Level.FULL;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><blockquote>
<p>配置文件方式</p>
</blockquote>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  client</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">#这里用default就是全局配置，如果是写服务名称，则是针对某个微服务的配置</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        loggerLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">FULL</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> #日志级别</span></span></code></pre>
</div><h2 id="进阶知识" tabindex="-1">进阶知识 <a class="header-anchor" href="#进阶知识" aria-label="Permalink to &quot;进阶知识&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">扩展封装</p>
<p>为了方便开发过程中减少繁琐的代码冗余问题，专门针对 <code>Feign</code> 请求做了下列功能的增强</p>
<ul>
<li>数据解码</li>
<li>代理请求</li>
<li>请求透传</li>
<li>自动刷新 TOKEN</li>
</ul>
</div>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>feign-plugin-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="数据解码" tabindex="-1">数据解码 <a class="header-anchor" href="#数据解码" aria-label="Permalink to &quot;数据解码&quot;">&ZeroWidthSpace;</a></h3>
<p>这是一个很实用的功能，很多时候我们希望写 <code>@FeignClient</code> 的时候规避掉 <strong><code>Result.class、R.class</code></strong> 统一类型，只想拿结果集直接处理</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>// 配置全局解码之前 </span></span>
<span class="line"><span>@GetMapping("/list")</span></span>
<span class="line"><span>Result&#x3C;List&#x3C;POJO>> list();</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// 配置全局解码之后</span></span>
<span class="line"><span>@GetMapping("/list")</span></span>
<span class="line"><span>List&#x3C;POJO> list();</span></span></code></pre>
</div><p><strong>上面的案例中，如果返回的统一对象还得先提取一下 <code>List&lt;POJO&gt; list = result.getData()</code> 还得判断空以免出现 <code>NPE</code> 的问题。</strong></p>
<p><strong>写多了同时会影响代码整洁，那么配置全局解码后即可忽略全局对象，同时默认采用 <code>SpringDecoder</code> 而非 <code>FeignDefaultDecoder</code> 更加贴合日常开发</strong></p>
<div class="danger custom-block"><p class="custom-block-title">注意</p>
<p>配置之后并不影响你写 <code>Result&lt;List&lt;POJO&gt;&gt;</code> 这样的代码，所以你无需担心，因为它是兼容的</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Decoder </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ObjectFactory</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">HttpMessageConverters</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> messageConverters,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                            ObjectProvider</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">HttpMessageConverterCustomizer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> customizers) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OptionalDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ResponseEntityDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FeignResponseDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SpringDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(messageConverters, customizers)))));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ErrorDecoder </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">errorDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (s, response) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"response status is:{}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ErrorDecoder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">decode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(s, response);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="请求透传" tabindex="-1">请求透传 <a class="header-anchor" href="#请求透传" aria-label="Permalink to &quot;请求透传&quot;">&ZeroWidthSpace;</a></h3>
<p>这个还是很有用的，不然 <code>FeignClient</code> 在发送 HTTP 请求的时候默认是不会携带上游 Header 的，意味着你后续接口如果有 Token 限制就会访问受限</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">999999</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FeignPluginInterceptor </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignPluginInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(FeignPluginProperties properties) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FeignPluginInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(properties);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="代理请求" tabindex="-1">代理请求 <a class="header-anchor" href="#代理请求" aria-label="Permalink to &quot;代理请求&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">一个很实用的功能 它可以使你在微服务场景下向单机一样的去写代码，在也不用苦恼开发阶段需要开一堆服务机器内存不够了</p>
<p><strong>可以让你在内网开发过程中，只需要启动编码的程序即可，其它服务会自动代理请求配置的 HTTP 地址去</strong></p>
<p><strong>举个例子：假设你跟很多小伙伴开发<code>订单服务</code>，它需要依赖<code>商品</code>、<code>认证</code>、<code>会员</code>等服务的接口，而你又不能将<code>订单服务</code>注册到测试环境中去，这时候自己启动一堆服务机器又卡，开发效率也低下，那么就可以用这种方式啦</strong></p>
</div>
<h3 id="配置启用" tabindex="-1">配置启用 <a class="header-anchor" href="#配置启用" aria-label="Permalink to &quot;配置启用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    plugin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      mock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          baseserver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 真实的请求地址</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            server-url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://192.168.1.1:5002/base/api</span></span></code></pre>
</div><h3 id="核心代码" tabindex="-1">核心代码 <a class="header-anchor" href="#核心代码" aria-label="Permalink to &quot;核心代码&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@Primary</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ConditionalOnProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(prefix </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MockProperties.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">MOCK_PREFIX</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "enabled"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, havingValue </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "true"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public Client </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoadBalancerClient loadBalancerClient, LoadBalancerClientFactory loadBalancerClientFactory,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                          List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoadBalancerFeignRequestTransformer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> transformers,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                          MockProperties mockProperties) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MockLoadBalancerFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Client.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            loadBalancerClient, loadBalancerClientFactory, transformers, mockProperties);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动刷新-token" tabindex="-1">自动刷新 Token <a class="header-anchor" href="#自动刷新-token" aria-label="Permalink to &quot;自动刷新 Token&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">解决内部服务调用时没有 Token 的尴尬情况</p>
<p><strong>比如定时任务调用访问受限的接口，这时候没有 <code>Token</code> 就自动根据配置的系统账号生成一个</strong></p>
</div>
<h4 id="配置启用-1" tabindex="-1">配置启用 <a class="header-anchor" href="#配置启用-1" aria-label="Permalink to &quot;配置启用&quot;">&ZeroWidthSpace;</a></h4>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    plugin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 是否为负载均衡请求（最终还是被我改成真实请求了）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        load-balance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # Feign 服务地址</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://wemirr-platform-iam/oauth2/token</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 如果定时任务需要动态设置租户目前没想到好的方案，欢迎提供思路和建议</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        o-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          client-id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">messaging-client</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          client-secret</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">admin</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          password</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          tenant-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0000"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          scope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">platform</span></span></code></pre>
</div><h4 id="核心代码-1" tabindex="-1">核心代码 <a class="header-anchor" href="#核心代码-1" aria-label="Permalink to &quot;核心代码&quot;">&ZeroWidthSpace;</a></h4>
<p><strong>基于 FeignInterceptor 做了拦截处理，在请求之前模拟登陆将 <code>Token</code> 存在本地缓存中，提高性能</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ConditionalOnProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AutoRefreshTokenProperties.TOKEN_PREFIX, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "enabled"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">havingValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "true"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AutoRefreshTokenInterceptor </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignTokenInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AutoRefreshTokenProperties properties) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AutoRefreshTokenProperties.Cache cache </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> properties.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> tokenCache </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CacheBuilder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newBuilder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">initialCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getInitialCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">maximumSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMaximumSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">expireAfterWrite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getExpire</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), TimeUnit.SECONDS).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AutoRefreshTokenInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(properties, tokenCache);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="feign-服务调用" tabindex="-1">Feign 服务调用 <a class="header-anchor" href="#feign-服务调用" aria-label="Permalink to &quot;Feign 服务调用&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">使用微服务必不可少会用到 <code>Feign</code>。 为了方便大家无感知使用请认真熟读本章节</p>
<p><code>Spring Cloud OpenFeign</code> 是Spring 官方为替代进入停更维护状态的 Feign 推出的另一款声明式服务调用与负载均衡组件。</p>
<p><strong>它具有 <code>Feign</code> 的所有功能，并在其基础上增加了对 <code>Spring MVC</code> 注解的支持。</strong></p>
</div>
<h2 id="基础知识" tabindex="-1">基础知识 <a class="header-anchor" href="#基础知识" aria-label="Permalink to &quot;基础知识&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>添加下面依赖即可快速使用 <code>Spring Cloud OpenFeign</code> 与负载均衡</strong></p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-openfeign&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-loadbalancer&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="负载均衡" tabindex="-1">负载均衡 <a class="header-anchor" href="#负载均衡" aria-label="Permalink to &quot;负载均衡&quot;">&ZeroWidthSpace;</a></h3>
<p>当需要用 <code>RestTemplate</code> 显示调用的时，需要给 <code>RestTemplate</code> 的实例上添加 <code>@LoadBalanced</code> 表示它是一个负载均衡的调用</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Primary</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">LoadBalanced</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RestTemplate </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbRestTemplate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RestTemplate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="性能优化" tabindex="-1">性能优化 <a class="header-anchor" href="#性能优化" aria-label="Permalink to &quot;性能优化&quot;">&ZeroWidthSpace;</a></h3>
<blockquote>
<p>Gzip 压缩</p>
</blockquote>
<div class="warning custom-block"><p class="custom-block-title">服务之间调用或者文件传输的时候开启压缩支持可以大大的提升响应效率</p>
<p>开启压缩可以有效节约网络资源，但是会增加CPU压力，建议把最小压缩的文档大小适度调大一点。</p>
</div>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  compression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    request</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 开启请求压缩</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 配置压缩支持的 MIME TYPE</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      mime-types</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">text/xml,application/xml,application/json</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 配置压缩数据大小的下限</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      min-request-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2048</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">   </span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 开启响应压缩</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><blockquote>
<p>连接池配置</p>
</blockquote>
<p>开启 <code>http-client</code> 配置替换默认的 <code>URLConnection</code> 同时配置连接池和超时时间，提高响应速度</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  httpclient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> # HttpClient的开关</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    connection-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3000</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 连接超时（毫秒）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    time-to-live</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                # 连接存活时长</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    time-to-live-unit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">minutes</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     # 连接存活时长单位</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    max-connections</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">500</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 最大连接数</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    max-connections-per-route</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 单个请求路径的最大连接数</span></span></code></pre>
</div><h3 id="详细日志" tabindex="-1">详细日志 <a class="header-anchor" href="#详细日志" aria-label="Permalink to &quot;详细日志&quot;">&ZeroWidthSpace;</a></h3>
<blockquote>
<p>Bean 注入的方式</p>
</blockquote>
<p>配置如下 <code>@Bean</code> 既可开启详细日志（不过在生产环境建议关掉,可以通过表达式控制 <code>@Bean</code> 在生产环境不注入）</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Logger.Level </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignLoggerLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Logger.Level.FULL;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><blockquote>
<p>配置文件方式</p>
</blockquote>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  client</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">#这里用default就是全局配置，如果是写服务名称，则是针对某个微服务的配置</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        loggerLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">FULL</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> #日志级别</span></span></code></pre>
</div><h2 id="进阶知识" tabindex="-1">进阶知识 <a class="header-anchor" href="#进阶知识" aria-label="Permalink to &quot;进阶知识&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">扩展封装</p>
<p>为了方便开发过程中减少繁琐的代码冗余问题，专门针对 <code>Feign</code> 请求做了下列功能的增强</p>
<ul>
<li>数据解码</li>
<li>代理请求</li>
<li>请求透传</li>
<li>自动刷新 TOKEN</li>
</ul>
</div>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>feign-plugin-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="数据解码" tabindex="-1">数据解码 <a class="header-anchor" href="#数据解码" aria-label="Permalink to &quot;数据解码&quot;">&ZeroWidthSpace;</a></h3>
<p>这是一个很实用的功能，很多时候我们希望写 <code>@FeignClient</code> 的时候规避掉 <strong><code>Result.class、R.class</code></strong> 统一类型，只想拿结果集直接处理</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>// 配置全局解码之前 </span></span>
<span class="line"><span>@GetMapping("/list")</span></span>
<span class="line"><span>Result&#x3C;List&#x3C;POJO>> list();</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// 配置全局解码之后</span></span>
<span class="line"><span>@GetMapping("/list")</span></span>
<span class="line"><span>List&#x3C;POJO> list();</span></span></code></pre>
</div><p><strong>上面的案例中，如果返回的统一对象还得先提取一下 <code>List&lt;POJO&gt; list = result.getData()</code> 还得判断空以免出现 <code>NPE</code> 的问题。</strong></p>
<p><strong>写多了同时会影响代码整洁，那么配置全局解码后即可忽略全局对象，同时默认采用 <code>SpringDecoder</code> 而非 <code>FeignDefaultDecoder</code> 更加贴合日常开发</strong></p>
<div class="danger custom-block"><p class="custom-block-title">注意</p>
<p>配置之后并不影响你写 <code>Result&lt;List&lt;POJO&gt;&gt;</code> 这样的代码，所以你无需担心，因为它是兼容的</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Decoder </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ObjectFactory</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">HttpMessageConverters</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> messageConverters,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                            ObjectProvider</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">HttpMessageConverterCustomizer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> customizers) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OptionalDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ResponseEntityDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FeignResponseDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SpringDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(messageConverters, customizers)))));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ErrorDecoder </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">errorDecoder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (s, response) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"response status is:{}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">status</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ErrorDecoder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">decode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(s, response);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="请求透传" tabindex="-1">请求透传 <a class="header-anchor" href="#请求透传" aria-label="Permalink to &quot;请求透传&quot;">&ZeroWidthSpace;</a></h3>
<p>这个还是很有用的，不然 <code>FeignClient</code> 在发送 HTTP 请求的时候默认是不会携带上游 Header 的，意味着你后续接口如果有 Token 限制就会访问受限</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">999999</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FeignPluginInterceptor </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignPluginInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(FeignPluginProperties properties) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FeignPluginInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(properties);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="代理请求" tabindex="-1">代理请求 <a class="header-anchor" href="#代理请求" aria-label="Permalink to &quot;代理请求&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">一个很实用的功能 它可以使你在微服务场景下向单机一样的去写代码，在也不用苦恼开发阶段需要开一堆服务机器内存不够了</p>
<p><strong>可以让你在内网开发过程中，只需要启动编码的程序即可，其它服务会自动代理请求配置的 HTTP 地址去</strong></p>
<p><strong>举个例子：假设你跟很多小伙伴开发<code>订单服务</code>，它需要依赖<code>商品</code>、<code>认证</code>、<code>会员</code>等服务的接口，而你又不能将<code>订单服务</code>注册到测试环境中去，这时候自己启动一堆服务机器又卡，开发效率也低下，那么就可以用这种方式啦</strong></p>
</div>
<h3 id="配置启用" tabindex="-1">配置启用 <a class="header-anchor" href="#配置启用" aria-label="Permalink to &quot;配置启用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    plugin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      mock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        server-map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          baseserver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 真实的请求地址</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">            server-url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://192.168.1.1:5002/base/api</span></span></code></pre>
</div><h3 id="核心代码" tabindex="-1">核心代码 <a class="header-anchor" href="#核心代码" aria-label="Permalink to &quot;核心代码&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@Primary</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ConditionalOnProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(prefix </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MockProperties.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">MOCK_PREFIX</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "enabled"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, havingValue </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "true"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public Client </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LoadBalancerClient loadBalancerClient, LoadBalancerClientFactory loadBalancerClientFactory,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                          List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoadBalancerFeignRequestTransformer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> transformers,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                          MockProperties mockProperties) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MockLoadBalancerFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Client.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            loadBalancerClient, loadBalancerClientFactory, transformers, mockProperties);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动刷新-token" tabindex="-1">自动刷新 Token <a class="header-anchor" href="#自动刷新-token" aria-label="Permalink to &quot;自动刷新 Token&quot;">&ZeroWidthSpace;</a></h3>
<div class="tip custom-block"><p class="custom-block-title">解决内部服务调用时没有 Token 的尴尬情况</p>
<p><strong>比如定时任务调用访问受限的接口，这时候没有 <code>Token</code> 就自动根据配置的系统账号生成一个</strong></p>
</div>
<h4 id="配置启用-1" tabindex="-1">配置启用 <a class="header-anchor" href="#配置启用-1" aria-label="Permalink to &quot;配置启用&quot;">&ZeroWidthSpace;</a></h4>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  feign</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    plugin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 是否为负载均衡请求（最终还是被我改成真实请求了）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        load-balance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # Feign 服务地址</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">http://wemirr-platform-iam/oauth2/token</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 如果定时任务需要动态设置租户目前没想到好的方案，欢迎提供思路和建议</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        o-auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          client-id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">messaging-client</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          client-secret</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">admin</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          password</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          tenant-code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0000"</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          scope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">platform</span></span></code></pre>
</div><h4 id="核心代码-1" tabindex="-1">核心代码 <a class="header-anchor" href="#核心代码-1" aria-label="Permalink to &quot;核心代码&quot;">&ZeroWidthSpace;</a></h4>
<p><strong>基于 FeignInterceptor 做了拦截处理，在请求之前模拟登陆将 <code>Token</code> 存在本地缓存中，提高性能</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ConditionalOnProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AutoRefreshTokenProperties.TOKEN_PREFIX, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "enabled"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">havingValue</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "true"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AutoRefreshTokenInterceptor </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">feignTokenInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AutoRefreshTokenProperties properties) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AutoRefreshTokenProperties.Cache cache </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> properties.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Cache&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> tokenCache </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CacheBuilder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newBuilder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">initialCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getInitialCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">maximumSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMaximumSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">expireAfterWrite</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(cache.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getExpire</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), TimeUnit.SECONDS).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AutoRefreshTokenInterceptor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(properties, tokenCache);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[框架组件总览 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/framework.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/framework.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="框架组件总览" tabindex="-1">框架组件总览 <a class="header-anchor" href="#框架组件总览" aria-label="Permalink to &quot;框架组件总览&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">说明</p>
<p>本文档介绍 <code>wemirr-platform-framework</code> 下的所有组件功能和使用方法</p>
</div>
<h2 id="组件清单" tabindex="-1">组件清单 <a class="header-anchor" href="#组件清单" aria-label="Permalink to &quot;组件清单&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
<th>依赖方式</th>
</tr>
</thead>
<tbody>
<tr>
<td>common-framework-core</td>
<td>基础核心</td>
<td>自动引入</td>
</tr>
<tr>
<td>common-spring-boot-starter</td>
<td>Spring Boot 启动器</td>
<td>手动引入</td>
</tr>
<tr>
<td>db-spring-boot-starter</td>
<td>数据库增强</td>
<td>手动引入</td>
</tr>
<tr>
<td>redis-plus-spring-boot-starter</td>
<td>Redis 增强</td>
<td>手动引入</td>
</tr>
<tr>
<td>security-spring-boot-starter</td>
<td>安全认证</td>
<td>手动引入</td>
</tr>
<tr>
<td>feign-plugin-spring-boot-starter</td>
<td>Feign 增强</td>
<td>手动引入</td>
</tr>
<tr>
<td>easyexcel-spring-boot-starter</td>
<td>Excel 操作</td>
<td>手动引入</td>
</tr>
<tr>
<td>diff-log-spring-boot-starter</td>
<td>差异日志</td>
<td>手动引入</td>
</tr>
<tr>
<td>i18n-spring-boot-starter</td>
<td>国际化</td>
<td>手动引入</td>
</tr>
<tr>
<td>robot-spring-boot-starter</td>
<td>消息机器人</td>
<td>手动引入</td>
</tr>
<tr>
<td>websocket-spring-boot-starter</td>
<td>WebSocket</td>
<td>手动引入</td>
</tr>
</tbody>
</table>
<h2 id="common-framework-core" tabindex="-1">common-framework-core <a class="header-anchor" href="#common-framework-core" aria-label="Permalink to &quot;common-framework-core&quot;">&ZeroWidthSpace;</a></h2>
<p>基础核心模块，提供公共注解、接口、异常和工具类。</p>
<h3 id="常用注解" tabindex="-1">常用注解 <a class="header-anchor" href="#常用注解" aria-label="Permalink to &quot;常用注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 操作日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "新增用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 租户忽略</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantIgnore</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">field</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">deptField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dept_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
</div><h3 id="统一响应" tabindex="-1">统一响应 <a class="header-anchor" href="#统一响应" aria-label="Permalink to &quot;统一响应&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 成功响应</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 失败响应</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"操作失败"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span></code></pre>
</div><h3 id="业务异常" tabindex="-1">业务异常 <a class="header-anchor" href="#业务异常" aria-label="Permalink to &quot;业务异常&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 抛出业务异常</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 断言</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEnabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户已禁用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h2 id="db-spring-boot-starter" tabindex="-1">db-spring-boot-starter <a class="header-anchor" href="#db-spring-boot-starter" aria-label="Permalink to &quot;db-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>数据库增强模块，集成 MyBatis-Plus 并提供多租户、数据权限支持。</p>
<h3 id="maven-依赖" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>db-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="多租户配置" tabindex="-1">多租户配置 <a class="header-anchor" href="#多租户配置" aria-label="Permalink to &quot;多租户配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">column</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">              # column / datasource</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      tenant-column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">tenant_id</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      ignore-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 忽略租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_tenant</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_dict</span></span></code></pre>
</div><h3 id="数据权限" tabindex="-1">数据权限 <a class="header-anchor" href="#数据权限" aria-label="Permalink to &quot;数据权限&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">field</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">deptField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dept_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildWrapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动填充" tabindex="-1">自动填充 <a class="header-anchor" href="#自动填充" aria-label="Permalink to &quot;自动填充&quot;">&ZeroWidthSpace;</a></h3>
<p>实体类继承 <code>SuperEntity</code> 或 <code>Entity</code> 自动填充审计字段：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_order"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // id, created_by, created_name, created_time, </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // updated_by, updated_name, updated_time 自动填充</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String orderNo;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BigDecimal amount;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="security-spring-boot-starter" tabindex="-1">security-spring-boot-starter <a class="header-anchor" href="#security-spring-boot-starter" aria-label="Permalink to &quot;security-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>安全认证模块，基于 Sa-Token 封装。</p>
<h3 id="maven-依赖-1" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-1" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>security-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="权限注解" tabindex="-1">权限注解 <a class="header-anchor" href="#权限注解" aria-label="Permalink to &quot;权限注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 需要登录</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckLogin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/info"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() { ... }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 需要权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) { ... }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 需要角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckRole</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"admin"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) { ... }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="获取当前用户" tabindex="-1">获取当前用户 <a class="header-anchor" href="#获取当前用户" aria-label="Permalink to &quot;获取当前用户&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="feign-plugin-spring-boot-starter" tabindex="-1">feign-plugin-spring-boot-starter <a class="header-anchor" href="#feign-plugin-spring-boot-starter" aria-label="Permalink to &quot;feign-plugin-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>Feign 增强模块，自动传递 Token 和请求头。</p>
<h3 id="maven-依赖-2" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-2" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>feign-plugin-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 定义 Feign 接口</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "wemirr-platform-iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 调用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeignClient userFeignClient;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Token 和租户信息自动传递</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userFeignClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="easyexcel-spring-boot-starter" tabindex="-1">easyexcel-spring-boot-starter <a class="header-anchor" href="#easyexcel-spring-boot-starter" aria-label="Permalink to &quot;easyexcel-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>Excel 导入导出模块，基于 EasyExcel 封装。</p>
<h3 id="maven-依赖-3" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-3" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>easyexcel-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="导出示例" tabindex="-1">导出示例 <a class="header-anchor" href="#导出示例" aria-label="Permalink to &quot;导出示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fileName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserExportVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">listForExport</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserExportVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExcelProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExcelProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExcelProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建时间"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LocalDateTime createdTime;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="导入示例" tabindex="-1">导入示例 <a class="header-anchor" href="#导入示例" aria-label="Permalink to &quot;导入示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/import"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> importUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserImportVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">batchImport</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(users);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="robot-spring-boot-starter" tabindex="-1">robot-spring-boot-starter <a class="header-anchor" href="#robot-spring-boot-starter" aria-label="Permalink to &quot;robot-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>消息机器人模块，支持钉钉、企业微信、飞书。</p>
<h3 id="maven-依赖-4" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-4" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>robot-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  robot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    dingtalk</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      webhook</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://oapi.dingtalk.com/robot/send?access_token=xxx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      secret</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">xxx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    wechat</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      webhook</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    feishu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      webhook</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://open.feishu.cn/open-apis/bot/v2/hook/xxx</span></span></code></pre>
</div><h3 id="发送消息" tabindex="-1">发送消息 <a class="header-anchor" href="#发送消息" aria-label="Permalink to &quot;发送消息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> NotifyService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RobotMessageSender messageSender;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendAlert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送到钉钉</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendDingTalk</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TextMessage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(content));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送到企业微信</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendWechat</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TextMessage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(content));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送到飞书</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendFeishu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TextMessage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(content));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="websocket-spring-boot-starter" tabindex="-1">websocket-spring-boot-starter <a class="header-anchor" href="#websocket-spring-boot-starter" aria-label="Permalink to &quot;websocket-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>分布式 WebSocket 模块，基于 Redis 实现多节点消息同步。</p>
<h3 id="maven-依赖-5" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-5" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>websocket-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="发送消息-1" tabindex="-1">发送消息 <a class="header-anchor" href="#发送消息-1" aria-label="Permalink to &quot;发送消息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MessageService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WebSocketMessageSender messageSender;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 发送给指定用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId, message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 广播给所有用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 发送给指定租户的所有用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendToTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendToTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId, message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/guide/packages/tenant.html">多租户配置</a> - 深入了解租户配置</li>
<li><a href="/zh/guide/packages/data_permission.html">数据权限</a> - 数据权限详解</li>
<li><a href="/zh/guide/packages/excel.html">Excel 操作</a> - Excel 导入导出详解</li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="框架组件总览" tabindex="-1">框架组件总览 <a class="header-anchor" href="#框架组件总览" aria-label="Permalink to &quot;框架组件总览&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">说明</p>
<p>本文档介绍 <code>wemirr-platform-framework</code> 下的所有组件功能和使用方法</p>
</div>
<h2 id="组件清单" tabindex="-1">组件清单 <a class="header-anchor" href="#组件清单" aria-label="Permalink to &quot;组件清单&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>组件</th>
<th>说明</th>
<th>依赖方式</th>
</tr>
</thead>
<tbody>
<tr>
<td>common-framework-core</td>
<td>基础核心</td>
<td>自动引入</td>
</tr>
<tr>
<td>common-spring-boot-starter</td>
<td>Spring Boot 启动器</td>
<td>手动引入</td>
</tr>
<tr>
<td>db-spring-boot-starter</td>
<td>数据库增强</td>
<td>手动引入</td>
</tr>
<tr>
<td>redis-plus-spring-boot-starter</td>
<td>Redis 增强</td>
<td>手动引入</td>
</tr>
<tr>
<td>security-spring-boot-starter</td>
<td>安全认证</td>
<td>手动引入</td>
</tr>
<tr>
<td>feign-plugin-spring-boot-starter</td>
<td>Feign 增强</td>
<td>手动引入</td>
</tr>
<tr>
<td>easyexcel-spring-boot-starter</td>
<td>Excel 操作</td>
<td>手动引入</td>
</tr>
<tr>
<td>diff-log-spring-boot-starter</td>
<td>差异日志</td>
<td>手动引入</td>
</tr>
<tr>
<td>i18n-spring-boot-starter</td>
<td>国际化</td>
<td>手动引入</td>
</tr>
<tr>
<td>robot-spring-boot-starter</td>
<td>消息机器人</td>
<td>手动引入</td>
</tr>
<tr>
<td>websocket-spring-boot-starter</td>
<td>WebSocket</td>
<td>手动引入</td>
</tr>
</tbody>
</table>
<h2 id="common-framework-core" tabindex="-1">common-framework-core <a class="header-anchor" href="#common-framework-core" aria-label="Permalink to &quot;common-framework-core&quot;">&ZeroWidthSpace;</a></h2>
<p>基础核心模块，提供公共注解、接口、异常和工具类。</p>
<h3 id="常用注解" tabindex="-1">常用注解 <a class="header-anchor" href="#常用注解" aria-label="Permalink to &quot;常用注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 操作日志</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AccessLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">module</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "新增用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 租户忽略</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantIgnore</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">field</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">deptField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dept_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre>
</div><h3 id="统一响应" tabindex="-1">统一响应 <a class="header-anchor" href="#统一响应" aria-label="Permalink to &quot;统一响应&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 成功响应</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 失败响应</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"操作失败"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span></code></pre>
</div><h3 id="业务异常" tabindex="-1">业务异常 <a class="header-anchor" href="#业务异常" aria-label="Permalink to &quot;业务异常&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 抛出业务异常</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ResultCode.USER_NOT_FOUND);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 断言</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">BizAssert.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isTrue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEnabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户已禁用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h2 id="db-spring-boot-starter" tabindex="-1">db-spring-boot-starter <a class="header-anchor" href="#db-spring-boot-starter" aria-label="Permalink to &quot;db-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>数据库增强模块，集成 MyBatis-Plus 并提供多租户、数据权限支持。</p>
<h3 id="maven-依赖" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>db-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="多租户配置" tabindex="-1">多租户配置 <a class="header-anchor" href="#多租户配置" aria-label="Permalink to &quot;多租户配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">column</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">              # column / datasource</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      tenant-column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">tenant_id</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      ignore-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:            </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 忽略租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_tenant</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_dict</span></span></code></pre>
</div><h3 id="数据权限" tabindex="-1">数据权限 <a class="header-anchor" href="#数据权限" aria-label="Permalink to &quot;数据权限&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">field</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">deptField</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "dept_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildWrapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="自动填充" tabindex="-1">自动填充 <a class="header-anchor" href="#自动填充" aria-label="Permalink to &quot;自动填充&quot;">&ZeroWidthSpace;</a></h3>
<p>实体类继承 <code>SuperEntity</code> 或 <code>Entity</code> 自动填充审计字段：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_order"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // id, created_by, created_name, created_time, </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // updated_by, updated_name, updated_time 自动填充</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String orderNo;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BigDecimal amount;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="security-spring-boot-starter" tabindex="-1">security-spring-boot-starter <a class="header-anchor" href="#security-spring-boot-starter" aria-label="Permalink to &quot;security-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>安全认证模块，基于 Sa-Token 封装。</p>
<h3 id="maven-依赖-1" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-1" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>security-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="权限注解" tabindex="-1">权限注解 <a class="header-anchor" href="#权限注解" aria-label="Permalink to &quot;权限注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 需要登录</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckLogin</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/info"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserVO </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() { ... }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 需要权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sys:user:add"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) { ... }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 需要角色</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SaCheckRole</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"admin"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DeleteMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> delete</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) { ... }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="获取当前用户" tabindex="-1">获取当前用户 <a class="header-anchor" href="#获取当前用户" aria-label="Permalink to &quot;获取当前用户&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createOrder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderDTO </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dto</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="feign-plugin-spring-boot-starter" tabindex="-1">feign-plugin-spring-boot-starter <a class="header-anchor" href="#feign-plugin-spring-boot-starter" aria-label="Permalink to &quot;feign-plugin-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>Feign 增强模块，自动传递 Token 和请求头。</p>
<h3 id="maven-依赖-2" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-2" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>feign-plugin-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 定义 Feign 接口</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "wemirr-platform-iam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserFeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 调用</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeignClient userFeignClient;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> process</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // Token 和租户信息自动传递</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Result&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userFeignClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1L</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="easyexcel-spring-boot-starter" tabindex="-1">easyexcel-spring-boot-starter <a class="header-anchor" href="#easyexcel-spring-boot-starter" aria-label="Permalink to &quot;easyexcel-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>Excel 导入导出模块，基于 EasyExcel 封装。</p>
<h3 id="maven-依赖-3" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-3" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>easyexcel-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="导出示例" tabindex="-1">导出示例 <a class="header-anchor" href="#导出示例" aria-label="Permalink to &quot;导出示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/export"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ResponseExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">fileName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "用户列表"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">UserExportVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserQuery </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">listForExport</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserExportVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExcelProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户名"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String username;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExcelProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"手机号"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String mobile;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">ExcelProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建时间"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> LocalDateTime createdTime;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="导入示例" tabindex="-1">导入示例 <a class="header-anchor" href="#导入示例" aria-label="Permalink to &quot;导入示例&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/import"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> importUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestExcel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">UserImportVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    userService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">batchImport</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(users);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="robot-spring-boot-starter" tabindex="-1">robot-spring-boot-starter <a class="header-anchor" href="#robot-spring-boot-starter" aria-label="Permalink to &quot;robot-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>消息机器人模块，支持钉钉、企业微信、飞书。</p>
<h3 id="maven-依赖-4" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-4" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>robot-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  robot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    dingtalk</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      webhook</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://oapi.dingtalk.com/robot/send?access_token=xxx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      secret</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">xxx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    wechat</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      webhook</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    feishu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      webhook</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">https://open.feishu.cn/open-apis/bot/v2/hook/xxx</span></span></code></pre>
</div><h3 id="发送消息" tabindex="-1">发送消息 <a class="header-anchor" href="#发送消息" aria-label="Permalink to &quot;发送消息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> NotifyService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RobotMessageSender messageSender;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendAlert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">content</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送到钉钉</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendDingTalk</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TextMessage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(content));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送到企业微信</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendWechat</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TextMessage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(content));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送到飞书</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendFeishu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TextMessage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(content));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="websocket-spring-boot-starter" tabindex="-1">websocket-spring-boot-starter <a class="header-anchor" href="#websocket-spring-boot-starter" aria-label="Permalink to &quot;websocket-spring-boot-starter&quot;">&ZeroWidthSpace;</a></h2>
<p>分布式 WebSocket 模块，基于 Redis 实现多节点消息同步。</p>
<h3 id="maven-依赖-5" tabindex="-1">Maven 依赖 <a class="header-anchor" href="#maven-依赖-5" aria-label="Permalink to &quot;Maven 依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>websocket-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="发送消息-1" tabindex="-1">发送消息 <a class="header-anchor" href="#发送消息-1" aria-label="Permalink to &quot;发送消息&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MessageService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> WebSocketMessageSender messageSender;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 发送给指定用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendToUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId, message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 广播给所有用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">broadcast</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 发送给指定租户的所有用户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sendToTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageSender.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendToTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId, message);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li><a href="/zh/guide/packages/tenant.html">多租户配置</a> - 深入了解租户配置</li>
<li><a href="/zh/guide/packages/data_permission.html">数据权限</a> - 数据权限详解</li>
<li><a href="/zh/guide/packages/excel.html">Excel 操作</a> - Excel 导入导出详解</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[限访名单 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/gateway/blacklist.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/gateway/blacklist.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="限访名单" tabindex="-1">限访名单 <a class="header-anchor" href="#限访名单" aria-label="Permalink to &quot;限访名单&quot;">&ZeroWidthSpace;</a></h1>
<p><strong>顾名思义，限流就是限制流量。通过限流，我们可以很好地控制系统的 QPS，从而达到保护系统的目的。
常见的限流算法有：<code>计数器</code>算法，<code>漏桶（Leaky Bucket）</code>算法，<code>令牌桶（Token Bucket）</code>算法。</strong></p>
<p><code>Spring Cloud Gateway</code> 官方提供了 <code>RequestRateLimiterGatewayFilterFactory</code> 过滤器工厂，使用 <code>Redis</code> 和 <code>Lua</code> 脚本实现了令牌桶的方式。</p>
<h2 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        locator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          lowerCaseServiceId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      routes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">lb://wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          predicates</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Path=/iam/**</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          filters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 黑白名单过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BlackWhiteList</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BLACK_LIST</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ip-list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1,0:0:0:0:0:0:0:1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ignore-intranet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="实现代码" tabindex="-1">实现代码 <a class="header-anchor" href="#实现代码" aria-label="Permalink to &quot;实现代码&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">须知</p>
<p>这个黑白名单拦截器是简单的实现，具体可以基于设计思路自行扩展</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.platform.gateway.filter;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.alibaba.fastjson2.JSON;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.alibaba.fastjson2.JSONObject;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.google.common.net.HttpHeaders;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.AllArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.extern.slf4j.Slf4j;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.filter.GatewayFilter;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Configuration;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.core.annotation.Order;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.http.HttpStatus;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.http.MediaType;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.http.server.reactive.ServerHttpResponse;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.web.server.ServerWebExchange;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> reactor.core.publisher.Mono;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.net.InetAddress;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.net.InetSocketAddress;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.util.List;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">/**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * XForwardedRemoteAddressResolver</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * 黑白名单过滤器</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@author</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> Levin</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">99</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BlackWhiteListGatewayFilterFactory</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AbstractGatewayFilterFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">BlackWhiteListGatewayFilterFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String DEFAULT_FILTER_NAME </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "BlackWhiteList"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DEFAULT_FILTER_NAME;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BlackWhiteListGatewayFilterFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        super</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Config.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> GatewayFilter </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Config </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (exchange, chain) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            InetSocketAddress remoteAddress </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> XForwardedRemoteAddressResolver.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">maxTrustedIndex</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resolve</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> InetAddress inetAddress </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> remoteAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            String ip </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> inetAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHostAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[访问者IP地址] - [{}]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (config.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isIgnoreIntranet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> inetAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSiteLocalAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[忽略内网IP] - {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, inetAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSiteLocalAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> chain.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (config.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BlackWhiteListType.BLACK_LIST) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> access </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> config.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getIpList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (access) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[访问受限，该地址在黑名单列表] - [{}]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                    return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> accessRestricted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (config.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BlackWhiteListType.WHITE_LIST) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> access </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> config.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getIpList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (access) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> chain.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[访问受限，该地址不在白名单列表] - [{}]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                    return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> accessRestricted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> chain.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">accessRestricted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ServerWebExchange </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">exchange</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ServerHttpResponse response </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getResponse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatusCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HttpStatus.FORBIDDEN);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHeaders</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        JSONObject result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> JSONObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"messageId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, HttpStatus.FORBIDDEN.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"message"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"访问受限，请联系管理员"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"successful"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"timestamp"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">writeWith</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bufferFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(JSON.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJSONBytes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(result))));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer maxTrustedIndex </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BlackWhiteListType type;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ignoreIntranet;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> ipList;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AllArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BlackWhiteListType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         * 黑名单</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         */</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        BLACK_LIST</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         * 白名单</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         */</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        WHITE_LIST</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="限访名单" tabindex="-1">限访名单 <a class="header-anchor" href="#限访名单" aria-label="Permalink to &quot;限访名单&quot;">&ZeroWidthSpace;</a></h1>
<p><strong>顾名思义，限流就是限制流量。通过限流，我们可以很好地控制系统的 QPS，从而达到保护系统的目的。
常见的限流算法有：<code>计数器</code>算法，<code>漏桶（Leaky Bucket）</code>算法，<code>令牌桶（Token Bucket）</code>算法。</strong></p>
<p><code>Spring Cloud Gateway</code> 官方提供了 <code>RequestRateLimiterGatewayFilterFactory</code> 过滤器工厂，使用 <code>Redis</code> 和 <code>Lua</code> 脚本实现了令牌桶的方式。</p>
<h2 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        locator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          lowerCaseServiceId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      routes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">lb://wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          predicates</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Path=/iam/**</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          filters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 黑白名单过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BlackWhiteList</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BLACK_LIST</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ip-list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1,0:0:0:0:0:0:0:1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ignore-intranet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="实现代码" tabindex="-1">实现代码 <a class="header-anchor" href="#实现代码" aria-label="Permalink to &quot;实现代码&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">须知</p>
<p>这个黑白名单拦截器是简单的实现，具体可以基于设计思路自行扩展</p>
</div>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.platform.gateway.filter;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.alibaba.fastjson2.JSON;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.alibaba.fastjson2.JSONObject;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.google.common.net.HttpHeaders;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.AllArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.Data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.extern.slf4j.Slf4j;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.filter.GatewayFilter;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Configuration;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.core.annotation.Order;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.http.HttpStatus;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.http.MediaType;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.http.server.reactive.ServerHttpResponse;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.web.server.ServerWebExchange;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> reactor.core.publisher.Mono;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.net.InetAddress;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.net.InetSocketAddress;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.util.List;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">/**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * XForwardedRemoteAddressResolver</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * 黑白名单过滤器</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@author</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> Levin</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">99</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BlackWhiteListGatewayFilterFactory</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AbstractGatewayFilterFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">BlackWhiteListGatewayFilterFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String DEFAULT_FILTER_NAME </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "BlackWhiteList"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DEFAULT_FILTER_NAME;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BlackWhiteListGatewayFilterFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        super</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Config.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> GatewayFilter </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Config </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (exchange, chain) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            InetSocketAddress remoteAddress </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> XForwardedRemoteAddressResolver.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">maxTrustedIndex</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resolve</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> InetAddress inetAddress </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> remoteAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            String ip </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> inetAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHostAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">debug</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[访问者IP地址] - [{}]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (config.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isIgnoreIntranet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> inetAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSiteLocalAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[忽略内网IP] - {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, inetAddress.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSiteLocalAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> chain.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (config.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BlackWhiteListType.BLACK_LIST) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> access </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> config.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getIpList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (access) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[访问受限，该地址在黑名单列表] - [{}]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                    return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> accessRestricted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (config.type </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BlackWhiteListType.WHITE_LIST) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> access </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> config.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getIpList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (access) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> chain.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    log.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">warn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"[访问受限，该地址不在白名单列表] - [{}]"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, ip);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                    return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> accessRestricted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> chain.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">accessRestricted</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ServerWebExchange </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">exchange</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ServerHttpResponse response </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getResponse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatusCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HttpStatus.FORBIDDEN);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHeaders</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        JSONObject result </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> JSONObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"messageId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, HttpStatus.FORBIDDEN.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"message"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"访问受限，请联系管理员"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"successful"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"timestamp"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, System.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">currentTimeMillis</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">writeWith</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bufferFactory</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(JSON.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toJSONBytes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(result))));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Config</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer maxTrustedIndex </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BlackWhiteListType type;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ignoreIntranet;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> ipList;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AllArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BlackWhiteListType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         * 黑名单</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         */</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        BLACK_LIST</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         * 白名单</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         */</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">        WHITE_LIST</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[网关路由 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/gateway/fallback.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/gateway/fallback.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="网关路由" tabindex="-1">网关路由 <a class="header-anchor" href="#网关路由" aria-label="Permalink to &quot;网关路由&quot;">&ZeroWidthSpace;</a></h1>
]]></description>
            <content:encoded><![CDATA[<h1 id="网关路由" tabindex="-1">网关路由 <a class="header-anchor" href="#网关路由" aria-label="Permalink to &quot;网关路由&quot;">&ZeroWidthSpace;</a></h1>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[服务网关 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/gateway/</link>
            <guid>https://docs.battcn.com/zh/guide/packages/gateway/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="服务网关" tabindex="-1">服务网关 <a class="header-anchor" href="#服务网关" aria-label="Permalink to &quot;服务网关&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="基本介绍" tabindex="-1">基本介绍 <a class="header-anchor" href="#基本介绍" aria-label="Permalink to &quot;基本介绍&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">Spring Cloud Gateway</p>
<p><code>Spring Cloud Gateway</code> 是 <code>Spring Cloud</code>团队为云服务生态系统构建的API网关。</p>
<p><code>Spring Cloud Gateway</code> 旨在提供一种简单而有效的方法来路由到API，并为它们提供跨领域的关注点，例如：安全性，监视/指标，限流等。</p>
</div>
<p><strong>微服务的应用可能部署在不同机房，不同地区，不同域名下。此时客户端（浏览器/手机/软件工具）想 要请求对应的服务，都需要知道机器的具体 IP 或者域名 URL，当微服务实例众多时，这是非常难以记忆的，对 于客户端来说也太复杂难以维护。
此时就有了网关，客户端相关的请求直接发送到网关，由网关根据请求标识 解析判断出具体的微服务地址，再把请求转发到微服务实例。这其中的记忆功能就全部交由网关来操作了。</strong></p>
<h2 id="核心设计" tabindex="-1">核心设计 <a class="header-anchor" href="#核心设计" aria-label="Permalink to &quot;核心设计&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">路由（Route）</p>
<p>路由是网关最基础的部分，路由信息由 ID、目标 URI、一组断言和一组过滤器组成。如果断言 路由为真，则说明请求的 URI 和配置匹配。</p>
</div>
<div class="tip custom-block"><p class="custom-block-title">断言（Predicate）</p>
<p><code>Spring Cloud Gateway</code> 中的断言函数输入类型是 <code>Spring 5.x</code> 框架中的 <code>ServerWebExchange</code>。</p>
<p><code>Spring Cloud Gateway</code> 中的断言函数允许开发者去定义匹配来自于 <code>Http Request</code> 中的任 何信息，比如请求头和参数等。</p>
</div>
<div class="tip custom-block"><p class="custom-block-title">过滤器（Filter）</p>
<p><code>Spring Cloud Gateway</code> 中的 Filter 分为两种类型，分别是 <code>Gateway Filter</code> 和 <code>Global Filter</code>。过滤器将会对请求和响应进行处理。</p>
</div>
<h2 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>本章主要是讲述 <code>Spring Cloud Gateway</code> 基础用法，详细以及进阶内容请参考官方文档</strong></p>
<h3 id="导入依赖" tabindex="-1">导入依赖 <a class="header-anchor" href="#导入依赖" aria-label="Permalink to &quot;导入依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-gateway&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="路由配置" tabindex="-1">路由配置 <a class="header-anchor" href="#路由配置" aria-label="Permalink to &quot;路由配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p><code>StripPrefix=1</code> 配置，表示网关转发到业务模块时候会自动截取前缀。</p>
</div>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        locator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          lowerCaseServiceId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      routes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">lb://wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          predicates</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Path=/iam/**</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          filters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 黑白名单过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BlackWhiteList</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BLACK_LIST</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ip-list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1,0:0:0:0:0:0:0:1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ignore-intranet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 限流过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">RequestRateLimiter</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.replenishRate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 允许用户每秒处理多少个请求</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.burstCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 令牌桶的容量，允许在一秒钟内完成的最大请求数</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                # 使用 IP 限流策略（使用 SpEL 按名称引用 bean）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                key-resolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"#{@ipKeyResolver}"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">StripPrefix=1</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">PreserveHostHeader</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 重试</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Retry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                statuses</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BAD_GATEWAY</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                backoff</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  firstBackoff</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">200ms</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  maxBackoff</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">500ms</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  factor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  basedOnPreviousValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  exceptions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TimeoutException</span></span></code></pre>
</div><div class="tip custom-block"><p class="custom-block-title">提示</p>
<p>目前已经存在 <code>wemirr-platform-gateway</code> 网关服务，用于路由转发、异常处理、限流、降级、接口、鉴权等等。</p>
</div>
<h2 id="路由规则" tabindex="-1">路由规则 <a class="header-anchor" href="#路由规则" aria-label="Permalink to &quot;路由规则&quot;">&ZeroWidthSpace;</a></h2>
<p><strong><code>Spring Cloud Gateway</code> 创建 <code>Route</code> 对象时， 使用<code>RoutePredicateFactory</code>创建<code>Predicate</code>对象，<code>Predicate</code>对象可以赋值给<code>Route</code>。</strong></p>
<ul>
<li>Spring Cloud Gateway包含许多内置的Route Predicate Factories。</li>
<li>所有这些断言都匹配 HTTP 请求的不同属性。</li>
<li>多个Route Predicate Factories可以通过逻辑与（and）结合起来一起使用。</li>
</ul>
<p><strong>路由断言工厂<code>RoutePredicateFactory</code>包含的主要实现类如图所示，包括<code>Datetime</code>、请求的远端地址、路由权重、请求头、Host 地址、请求方法、请求路径和请求参数等类型的路由断言。</strong></p>
]]></description>
            <content:encoded><![CDATA[<h1 id="服务网关" tabindex="-1">服务网关 <a class="header-anchor" href="#服务网关" aria-label="Permalink to &quot;服务网关&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="基本介绍" tabindex="-1">基本介绍 <a class="header-anchor" href="#基本介绍" aria-label="Permalink to &quot;基本介绍&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">Spring Cloud Gateway</p>
<p><code>Spring Cloud Gateway</code> 是 <code>Spring Cloud</code>团队为云服务生态系统构建的API网关。</p>
<p><code>Spring Cloud Gateway</code> 旨在提供一种简单而有效的方法来路由到API，并为它们提供跨领域的关注点，例如：安全性，监视/指标，限流等。</p>
</div>
<p><strong>微服务的应用可能部署在不同机房，不同地区，不同域名下。此时客户端（浏览器/手机/软件工具）想 要请求对应的服务，都需要知道机器的具体 IP 或者域名 URL，当微服务实例众多时，这是非常难以记忆的，对 于客户端来说也太复杂难以维护。
此时就有了网关，客户端相关的请求直接发送到网关，由网关根据请求标识 解析判断出具体的微服务地址，再把请求转发到微服务实例。这其中的记忆功能就全部交由网关来操作了。</strong></p>
<h2 id="核心设计" tabindex="-1">核心设计 <a class="header-anchor" href="#核心设计" aria-label="Permalink to &quot;核心设计&quot;">&ZeroWidthSpace;</a></h2>
<div class="tip custom-block"><p class="custom-block-title">路由（Route）</p>
<p>路由是网关最基础的部分，路由信息由 ID、目标 URI、一组断言和一组过滤器组成。如果断言 路由为真，则说明请求的 URI 和配置匹配。</p>
</div>
<div class="tip custom-block"><p class="custom-block-title">断言（Predicate）</p>
<p><code>Spring Cloud Gateway</code> 中的断言函数输入类型是 <code>Spring 5.x</code> 框架中的 <code>ServerWebExchange</code>。</p>
<p><code>Spring Cloud Gateway</code> 中的断言函数允许开发者去定义匹配来自于 <code>Http Request</code> 中的任 何信息，比如请求头和参数等。</p>
</div>
<div class="tip custom-block"><p class="custom-block-title">过滤器（Filter）</p>
<p><code>Spring Cloud Gateway</code> 中的 Filter 分为两种类型，分别是 <code>Gateway Filter</code> 和 <code>Global Filter</code>。过滤器将会对请求和响应进行处理。</p>
</div>
<h2 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>本章主要是讲述 <code>Spring Cloud Gateway</code> 基础用法，详细以及进阶内容请参考官方文档</strong></p>
<h3 id="导入依赖" tabindex="-1">导入依赖 <a class="header-anchor" href="#导入依赖" aria-label="Permalink to &quot;导入依赖&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.cloud&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-cloud-starter-gateway&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="路由配置" tabindex="-1">路由配置 <a class="header-anchor" href="#路由配置" aria-label="Permalink to &quot;路由配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">WARNING</p>
<p><code>StripPrefix=1</code> 配置，表示网关转发到业务模块时候会自动截取前缀。</p>
</div>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        locator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          lowerCaseServiceId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      routes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">lb://wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          predicates</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Path=/iam/**</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          filters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 黑白名单过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BlackWhiteList</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BLACK_LIST</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ip-list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">127.0.0.1,0:0:0:0:0:0:0:1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                ignore-intranet</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 限流过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">RequestRateLimiter</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.replenishRate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 允许用户每秒处理多少个请求</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.burstCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 令牌桶的容量，允许在一秒钟内完成的最大请求数</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                # 使用 IP 限流策略（使用 SpEL 按名称引用 bean）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                key-resolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"#{@ipKeyResolver}"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">StripPrefix=1</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">PreserveHostHeader</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 重试</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Retry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                retries</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                statuses</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">BAD_GATEWAY</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                backoff</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  firstBackoff</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">200ms</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  maxBackoff</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">500ms</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  factor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  basedOnPreviousValue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                  exceptions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">TimeoutException</span></span></code></pre>
</div><div class="tip custom-block"><p class="custom-block-title">提示</p>
<p>目前已经存在 <code>wemirr-platform-gateway</code> 网关服务，用于路由转发、异常处理、限流、降级、接口、鉴权等等。</p>
</div>
<h2 id="路由规则" tabindex="-1">路由规则 <a class="header-anchor" href="#路由规则" aria-label="Permalink to &quot;路由规则&quot;">&ZeroWidthSpace;</a></h2>
<p><strong><code>Spring Cloud Gateway</code> 创建 <code>Route</code> 对象时， 使用<code>RoutePredicateFactory</code>创建<code>Predicate</code>对象，<code>Predicate</code>对象可以赋值给<code>Route</code>。</strong></p>
<ul>
<li>Spring Cloud Gateway包含许多内置的Route Predicate Factories。</li>
<li>所有这些断言都匹配 HTTP 请求的不同属性。</li>
<li>多个Route Predicate Factories可以通过逻辑与（and）结合起来一起使用。</li>
</ul>
<p><strong>路由断言工厂<code>RoutePredicateFactory</code>包含的主要实现类如图所示，包括<code>Datetime</code>、请求的远端地址、路由权重、请求头、Host 地址、请求方法、请求路径和请求参数等类型的路由断言。</strong></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[限流规则 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/gateway/limit.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/gateway/limit.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="限流规则" tabindex="-1">限流规则 <a class="header-anchor" href="#限流规则" aria-label="Permalink to &quot;限流规则&quot;">&ZeroWidthSpace;</a></h1>
<p><strong>顾名思义，限流就是限制流量。通过限流，我们可以很好地控制系统的 QPS，从而达到保护系统的目的。
常见的限流算法有：<code>计数器</code>算法，<code>漏桶（Leaky Bucket）</code>算法，<code>令牌桶（Token Bucket）</code>算法。</strong></p>
<p><code>Spring Cloud Gateway</code> 官方提供了 <code>RequestRateLimiterGatewayFilterFactory</code> 过滤器工厂，使用 <code>Redis</code> 和 <code>Lua</code> 脚本实现了令牌桶的方式。</p>
<h2 id="添加依赖" tabindex="-1">添加依赖 <a class="header-anchor" href="#添加依赖" aria-label="Permalink to &quot;添加依赖&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.boot&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-boot-starter-data-redis-reactive&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        locator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          lowerCaseServiceId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      routes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">lb://wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          predicates</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Path=/iam/**</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          filters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 限流过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">RequestRateLimiter</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.replenishRate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 允许用户每秒处理多少个请求</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.burstCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 令牌桶的容量，允许在一秒钟内完成的最大请求数</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                # 使用 IP 限流策略（使用 SpEL 按名称引用 bean）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                key-resolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"#{@ipKeyResolver}"</span></span></code></pre>
</div><h2 id="实现代码" tabindex="-1">实现代码 <a class="header-anchor" href="#实现代码" aria-label="Permalink to &quot;实现代码&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.platform.gateway.config;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Bean;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Configuration;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Primary;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> reactor.core.publisher.Mono;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.util.Objects;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">/**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@author</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> Levin</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> KeyResolverConfiguration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String AUTHORIZATION </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "authorization"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * IP 限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> IP限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Primary</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "ipKeyResolver"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> KeyResolver </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ipKeyResolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Objects.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">requireNonNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRemoteAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHostName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 用户限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 使用这种方式限流，请求路径中必须携带userId参数</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 用户限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "tokenKeyResolver"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> KeyResolver </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tokenKeyResolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Objects.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">requireNonNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHeaders</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFirst</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AUTHORIZATION)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 接口限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 获取请求地址的uri作为限流key</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 接口限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "apiKeyResolver"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> KeyResolver </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apiKeyResolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="限流规则" tabindex="-1">限流规则 <a class="header-anchor" href="#限流规则" aria-label="Permalink to &quot;限流规则&quot;">&ZeroWidthSpace;</a></h1>
<p><strong>顾名思义，限流就是限制流量。通过限流，我们可以很好地控制系统的 QPS，从而达到保护系统的目的。
常见的限流算法有：<code>计数器</code>算法，<code>漏桶（Leaky Bucket）</code>算法，<code>令牌桶（Token Bucket）</code>算法。</strong></p>
<p><code>Spring Cloud Gateway</code> 官方提供了 <code>RequestRateLimiterGatewayFilterFactory</code> 过滤器工厂，使用 <code>Redis</code> 和 <code>Lua</code> 脚本实现了令牌桶的方式。</p>
<h2 id="添加依赖" tabindex="-1">添加依赖 <a class="header-anchor" href="#添加依赖" aria-label="Permalink to &quot;添加依赖&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>org.springframework.boot&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>spring-boot-starter-data-redis-reactive&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  application</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-gateway</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  cloud</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    gateway</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      discovery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        locator</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          lowerCaseServiceId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      routes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          uri</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">lb://wemirr-platform-iam</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          predicates</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Path=/iam/**</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          filters</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            # 限流过滤器</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            - </span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">RequestRateLimiter</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">              args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.replenishRate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 允许用户每秒处理多少个请求</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                redis-rate-limiter.burstCapacity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">   # 令牌桶的容量，允许在一秒钟内完成的最大请求数</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                # 使用 IP 限流策略（使用 SpEL 按名称引用 bean）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">                key-resolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"#{@ipKeyResolver}"</span></span></code></pre>
</div><h2 id="实现代码" tabindex="-1">实现代码 <a class="header-anchor" href="#实现代码" aria-label="Permalink to &quot;实现代码&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.platform.gateway.config;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Bean;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Configuration;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.context.annotation.Primary;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> reactor.core.publisher.Mono;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> java.util.Objects;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">/**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@author</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> Levin</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Configuration</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> KeyResolverConfiguration</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String AUTHORIZATION </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "authorization"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * IP 限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> IP限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Primary</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "ipKeyResolver"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> KeyResolver </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ipKeyResolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Objects.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">requireNonNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRemoteAddress</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHostName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 用户限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 使用这种方式限流，请求路径中必须携带userId参数</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 用户限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "tokenKeyResolver"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> KeyResolver </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tokenKeyResolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Objects.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">requireNonNull</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getHeaders</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFirst</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AUTHORIZATION)));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 接口限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 获取请求地址的uri作为限流key</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 接口限流</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Bean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "apiKeyResolver"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> KeyResolver </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">apiKeyResolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> exchange </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Mono.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">just</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(exchange.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[无题]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/generate.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/generate.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[无题]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/introduction.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/introduction.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[存储 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/oss.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/oss.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="存储" tabindex="-1">存储 <a class="header-anchor" href="#存储" aria-label="Permalink to &quot;存储&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>支持<code>阿里云</code>、<code>腾讯云</code>、<code>自建Minio</code>、<code>七牛云</code>、等多种云存储，提供丰富的API开箱即用，如不满足业务需要，可以自行扩展<code>storage-spring-boot-starter</code></p>
</div>
<h2 id="使用方法" tabindex="-1">使用方法 <a class="header-anchor" href="#使用方法" aria-label="Permalink to &quot;使用方法&quot;">&ZeroWidthSpace;</a></h2>
]]></description>
            <content:encoded><![CDATA[<h1 id="存储" tabindex="-1">存储 <a class="header-anchor" href="#存储" aria-label="Permalink to &quot;存储&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>支持<code>阿里云</code>、<code>腾讯云</code>、<code>自建Minio</code>、<code>七牛云</code>、等多种云存储，提供丰富的API开箱即用，如不满足业务需要，可以自行扩展<code>storage-spring-boot-starter</code></p>
</div>
<h2 id="使用方法" tabindex="-1">使用方法 <a class="header-anchor" href="#使用方法" aria-label="Permalink to &quot;使用方法&quot;">&ZeroWidthSpace;</a></h2>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[远程映射 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/remote.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/remote.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="远程映射" tabindex="-1">远程映射 <a class="header-anchor" href="#远程映射" aria-label="Permalink to &quot;远程映射&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">解决跨服务字段映射问题</p>
<p><strong>当你不想在自己的业务代码入侵太多的 GET、SET 代码可以采用下面的方式，动态去映射字段结果集</strong></p>
<p><strong>有更好的设计意见和思路欢迎反馈</strong></p>
</div>
<h2 id="差异分析" tabindex="-1">差异分析 <a class="header-anchor" href="#差异分析" aria-label="Permalink to &quot;差异分析&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>大大提高开发效率，去掉冗余的代码</strong></p>
<h3 id="使用之前" tabindex="-1">使用之前 <a class="header-anchor" href="#使用之前" aria-label="Permalink to &quot;使用之前&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Resp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Resp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Map</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Long, UserInfoResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> map </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> feignClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">findByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ids);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Item item : list) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            UserInfoResp info </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> map.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (info </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                continue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(info.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setAvatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(info.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAvatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // .... 更多的赋值动作</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用之后" tabindex="-1">使用之后 <a class="header-anchor" href="#使用之后" aria-label="Permalink to &quot;使用之后&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>该方式不是万能的，目前只支持批量 KEY 的方式，同时要目标服务提供相应的接口才可以</p>
<p><strong>在需要动态映射的方式中添加 <code>@RemoteResult</code></strong></p>
<p><strong>在需要动态映射的实体中添加 <code>@Remote</code> （支持单个或多个字段映射）</strong></p>
</div>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @RemoteResult</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Resp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="注解讲解" tabindex="-1">注解讲解 <a class="header-anchor" href="#注解讲解" aria-label="Permalink to &quot;注解讲解&quot;">&ZeroWidthSpace;</a></h2>
<p><strong><code>@Remote</code>主要目的就是用来告诉 AOP 需要解析和映射的字段是什么</strong></p>
<p><strong><code>beanName</code> 添加 <code>非跨服务的 Bean Name</code> 注解的接口类，比如你自己服务某个 <code>XXXServiceImpl</code></strong></p>
<p><strong><code>beanClass</code> 添加 <code>@FeignClient</code> 注解的接口类</strong></p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Resp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Remote</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(beanClass </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeign.class, fields </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @Remote.FieldRef(source </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "nickName"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, target </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @Remote.FieldRef(source </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "avatar"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, target </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "avatar"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">avatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FeignConstants.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">FEIGN_NAME</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dismiss404 </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserFeign</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> LoadService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">UserInfoResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 根据 ID 批量查询</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ids</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 唯一键（可能不是主键ID)</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 查询结果</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/batch_ids"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, UserInfoResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> findByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@RequestBody </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">Set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ids</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="远程映射" tabindex="-1">远程映射 <a class="header-anchor" href="#远程映射" aria-label="Permalink to &quot;远程映射&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">解决跨服务字段映射问题</p>
<p><strong>当你不想在自己的业务代码入侵太多的 GET、SET 代码可以采用下面的方式，动态去映射字段结果集</strong></p>
<p><strong>有更好的设计意见和思路欢迎反馈</strong></p>
</div>
<h2 id="差异分析" tabindex="-1">差异分析 <a class="header-anchor" href="#差异分析" aria-label="Permalink to &quot;差异分析&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>大大提高开发效率，去掉冗余的代码</strong></p>
<h3 id="使用之前" tabindex="-1">使用之前 <a class="header-anchor" href="#使用之前" aria-label="Permalink to &quot;使用之前&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Resp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Resp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Map</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Long, UserInfoResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> map </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> feignClient.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">findByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ids);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Item item : list) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            UserInfoResp info </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> map.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (info </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">                continue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(info.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setAvatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(info.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAvatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // .... 更多的赋值动作</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="使用之后" tabindex="-1">使用之后 <a class="header-anchor" href="#使用之后" aria-label="Permalink to &quot;使用之后&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>该方式不是万能的，目前只支持批量 KEY 的方式，同时要目标服务提供相应的接口才可以</p>
<p><strong>在需要动态映射的方式中添加 <code>@RemoteResult</code></strong></p>
<p><strong>在需要动态映射的实体中添加 <code>@Remote</code> （支持单个或多个字段映射）</strong></p>
</div>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ServiceImpl</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @RemoteResult</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Resp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">list</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="注解讲解" tabindex="-1">注解讲解 <a class="header-anchor" href="#注解讲解" aria-label="Permalink to &quot;注解讲解&quot;">&ZeroWidthSpace;</a></h2>
<p><strong><code>@Remote</code>主要目的就是用来告诉 AOP 需要解析和映射的字段是什么</strong></p>
<p><strong><code>beanName</code> 添加 <code>非跨服务的 Bean Name</code> 注解的接口类，比如你自己服务某个 <code>XXXServiceImpl</code></strong></p>
<p><strong><code>beanClass</code> 添加 <code>@FeignClient</code> 注解的接口类</strong></p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Resp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Remote</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(beanClass </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserFeign.class, fields </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @Remote.FieldRef(source </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "nickName"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, target </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @Remote.FieldRef(source </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "avatar"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, target </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "avatar"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">avatar</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">FeignClient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> FeignConstants.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">FEIGN_NAME</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, dismiss404 </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">public </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> UserFeign</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> LoadService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">UserInfoResp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 根据 ID 批量查询</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ids</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 唯一键（可能不是主键ID)</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@return</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> 查询结果</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/users/batch_ids"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, UserInfoResp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> findByIds</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@RequestBody </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">Set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ids</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[消息机器人 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/robot.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/robot.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="消息机器人" tabindex="-1">消息机器人 <a class="header-anchor" href="#消息机器人" aria-label="Permalink to &quot;消息机器人&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">extend:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  robot:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ding-talk:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 填写钉钉 token</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      access-token:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ding-talk-access-token</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    we-chat:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      key:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 123</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    fei-shu:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      key:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 飞书申请</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      secret:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 飞书申请,如果不校验签名就可以不配置</span></span></code></pre>
</div><h2 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> messageNotify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Map</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String, Object</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> variables) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 获取所有的通知通道</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RobotMessageHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> beans </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SpringUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBeansOfType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RobotMessageHandler.class);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (beans </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (RobotMessageHandler messageHandler </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> beans.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送通知</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageHandler.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(notifyContent.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getExpressionText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), variables, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="消息机器人" tabindex="-1">消息机器人 <a class="header-anchor" href="#消息机器人" aria-label="Permalink to &quot;消息机器人&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="添加配置" tabindex="-1">添加配置 <a class="header-anchor" href="#添加配置" aria-label="Permalink to &quot;添加配置&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-shell vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">shell</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">extend:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  robot:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    ding-talk:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 填写钉钉 token</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      access-token:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ding-talk-access-token</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    we-chat:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      key:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 123</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    fei-shu:</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      enabled:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      key:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 飞书申请</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      secret:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 飞书申请,如果不校验签名就可以不配置</span></span></code></pre>
</div><h2 id="使用方式" tabindex="-1">使用方式 <a class="header-anchor" href="#使用方式" aria-label="Permalink to &quot;使用方式&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> messageNotify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Map</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">String, Object</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> variables) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 获取所有的通知通道</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RobotMessageHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> beans </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SpringUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBeansOfType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RobotMessageHandler.class);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (beans </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (RobotMessageHandler messageHandler </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> beans.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">values</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送通知</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        messageHandler.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(notifyContent.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getExpressionText</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), variables, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[安全中心 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/security/</link>
            <guid>https://docs.battcn.com/zh/guide/packages/security/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="安全中心" tabindex="-1">安全中心 <a class="header-anchor" href="#安全中心" aria-label="Permalink to &quot;安全中心&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="通用配置" tabindex="-1">通用配置 <a class="header-anchor" href="#通用配置" aria-label="Permalink to &quot;通用配置&quot;">&ZeroWidthSpace;</a></h2>
<p>鉴权采用的是 <code>sa-token</code>，可以灵活的实现<code>OAuth2</code>、<code>SSO</code> 等登录方式,鉴于大部分人玩不来,默认平台启用普通登录,
可以轻松配置<code>短信</code>、<code>验证码</code>、<code>GITEE</code>、<code>微信</code>等登录，如需要自己实现 <code>OAuth2</code>、<code>SSO</code> 可以参考 <a href="https://sa-token.cc/doc.html" target="_blank" rel="noreferrer">https://sa-token.cc/doc.html</a></p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">############## Sa-Token 配置 (文档: https://sa-token.cc) ##############</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">sa-token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 名称（同时也是 cookie 名称）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #  token-name: wp-token</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Authorization</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Bearer</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 有效期（单位：秒） 默认30天，-1 代表永久有效</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 最低活跃频率（单位：秒），如果 token 超过此时间没有访问系统就会被冻结，默认-1 代表不限制，永不冻结</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  active-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">-1</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否允许同一账号多地同时登录 （为 true 时允许一起登录, 为 false 时新登录挤掉旧登录）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-concurrent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 在多人登录同一账号时，是否共用一个 token （为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-share</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 风格（默认可取值：uuid、simple-uuid、random-32、random-64、random-128、tik）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">uuid</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否输出操作日志</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="访问受限" tabindex="-1">访问受限 <a class="header-anchor" href="#访问受限" aria-label="Permalink to &quot;访问受限&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li>检查是否满足访问权限，是否有 TOKEN 拦截，如果有拦截那么没 TOKEN 则需要配置下面的放行规则或者通过 <code>@SaIgnore</code> 进行放行</li>
</ul>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  oauth2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 配置权限过滤的地址</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resource-urls</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/captcha</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/instances/**</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="安全中心" tabindex="-1">安全中心 <a class="header-anchor" href="#安全中心" aria-label="Permalink to &quot;安全中心&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="通用配置" tabindex="-1">通用配置 <a class="header-anchor" href="#通用配置" aria-label="Permalink to &quot;通用配置&quot;">&ZeroWidthSpace;</a></h2>
<p>鉴权采用的是 <code>sa-token</code>，可以灵活的实现<code>OAuth2</code>、<code>SSO</code> 等登录方式,鉴于大部分人玩不来,默认平台启用普通登录,
可以轻松配置<code>短信</code>、<code>验证码</code>、<code>GITEE</code>、<code>微信</code>等登录，如需要自己实现 <code>OAuth2</code>、<code>SSO</code> 可以参考 <a href="https://sa-token.cc/doc.html" target="_blank" rel="noreferrer">https://sa-token.cc/doc.html</a></p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">############## Sa-Token 配置 (文档: https://sa-token.cc) ##############</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">sa-token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 名称（同时也是 cookie 名称）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #  token-name: wp-token</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Authorization</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Bearer</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 有效期（单位：秒） 默认30天，-1 代表永久有效</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 最低活跃频率（单位：秒），如果 token 超过此时间没有访问系统就会被冻结，默认-1 代表不限制，永不冻结</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  active-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">-1</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否允许同一账号多地同时登录 （为 true 时允许一起登录, 为 false 时新登录挤掉旧登录）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-concurrent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 在多人登录同一账号时，是否共用一个 token （为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-share</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 风格（默认可取值：uuid、simple-uuid、random-32、random-64、random-128、tik）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">uuid</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否输出操作日志</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="访问受限" tabindex="-1">访问受限 <a class="header-anchor" href="#访问受限" aria-label="Permalink to &quot;访问受限&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li>检查是否满足访问权限，是否有 TOKEN 拦截，如果有拦截那么没 TOKEN 则需要配置下面的放行规则或者通过 <code>@SaIgnore</code> 进行放行</li>
</ul>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  oauth2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 配置权限过滤的地址</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      resource-urls</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/captcha</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/instances/**</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Login 管理 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/security/login.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/security/login.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="login-管理" tabindex="-1">Login 管理 <a class="header-anchor" href="#login-管理" aria-label="Permalink to &quot;Login 管理&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="自定义登录类型" tabindex="-1">自定义登录类型 <a class="header-anchor" href="#自定义登录类型" aria-label="Permalink to &quot;自定义登录类型&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>默认支持 <code>密码</code>、<code>验证码</code>、<code>短信</code>、<code>GITEE</code> 等多种登录方式，除此之外提供了接口可以快速实现自定义登录</strong></p>
<blockquote>
<p>登录类型扩展</p>
</blockquote>
<p><strong>实现 <code>AuthenticatorStrategy</code> 接口即可自定义N种基于 <code>loginType</code> 方式的自定义登录</strong></p>
<h2 id="案例-短信登录" tabindex="-1">案例：短信登录 <a class="header-anchor" href="#案例-短信登录" aria-label="Permalink to &quot;案例：短信登录&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.commons.exception.CheckedException;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.security.configuration.server.support.AuthenticationPrincipal;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.security.configuration.server.support.AuthenticatorStrategy;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.RequiredArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.extern.slf4j.Slf4j;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.stereotype.Component;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">/**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * 短信验证码认证.</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@author</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> Levin</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> **/</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SmsAuthenticator</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AuthenticatorStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationPrincipal </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">principal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // username = 手机号</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> principal.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // password = 短信验证码</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String password </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> principal.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 验证 短信验证码</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> smsService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(username, password);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">status) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"短信验证码异常"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> authenticate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationPrincipal </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">principal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // TODO 自定义一些逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">loginType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "sms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="请求方式" tabindex="-1">请求方式 <a class="header-anchor" href="#请求方式" aria-label="Permalink to &quot;请求方式&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">POST => https:</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">//cloud.battcn.com/api/iam/token/login</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "tenantCode"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0000"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "username"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"13000000000"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "password"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"短信验证码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "loginType"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "clientId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"pc-web"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "clientSecret"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"pc-web"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="login-管理" tabindex="-1">Login 管理 <a class="header-anchor" href="#login-管理" aria-label="Permalink to &quot;Login 管理&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="自定义登录类型" tabindex="-1">自定义登录类型 <a class="header-anchor" href="#自定义登录类型" aria-label="Permalink to &quot;自定义登录类型&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>默认支持 <code>密码</code>、<code>验证码</code>、<code>短信</code>、<code>GITEE</code> 等多种登录方式，除此之外提供了接口可以快速实现自定义登录</strong></p>
<blockquote>
<p>登录类型扩展</p>
</blockquote>
<p><strong>实现 <code>AuthenticatorStrategy</code> 接口即可自定义N种基于 <code>loginType</code> 方式的自定义登录</strong></p>
<h2 id="案例-短信登录" tabindex="-1">案例：短信登录 <a class="header-anchor" href="#案例-短信登录" aria-label="Permalink to &quot;案例：短信登录&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.commons.exception.CheckedException;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.security.configuration.server.support.AuthenticationPrincipal;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> com.wemirr.framework.security.configuration.server.support.AuthenticatorStrategy;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.RequiredArgsConstructor;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lombok.extern.slf4j.Slf4j;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> org.springframework.stereotype.Component;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">/**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * 短信验证码认证.</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> *</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> * </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@author</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> Levin</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> **/</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SmsAuthenticator</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> AuthenticatorStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationPrincipal </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">principal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // username = 手机号</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> principal.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // password = 短信验证码</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        String password </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> principal.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 验证 短信验证码</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> status </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> smsService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(username, password);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">status) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"短信验证码异常"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> authenticate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationPrincipal </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">principal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // TODO 自定义一些逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">loginType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "sms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="请求方式" tabindex="-1">请求方式 <a class="header-anchor" href="#请求方式" aria-label="Permalink to &quot;请求方式&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">POST => https:</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">//cloud.battcn.com/api/iam/token/login</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "tenantCode"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0000"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "username"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"13000000000"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "password"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"短信验证码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "loginType"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"sms"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "clientId"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"pc-web"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  "clientSecret"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"pc-web"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[TOKEN 管理 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/security/token.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/security/token.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="token-管理" tabindex="-1">TOKEN 管理 <a class="header-anchor" href="#token-管理" aria-label="Permalink to &quot;TOKEN 管理&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="导入核心包" tabindex="-1">导入核心包 <a class="header-anchor" href="#导入核心包" aria-label="Permalink to &quot;导入核心包&quot;">&ZeroWidthSpace;</a></h2>
<p>本 SDK 基于 <code>sa-token</code>提供的最新SDK <code>sa-token-spring-boot3-starter:1.39.0</code> 集成封装,简化开发者使用</p>
<p><strong>即便默认没提供 <code>OAuth2.0</code> 授权协议，但是采用 <code>sa-token</code> 后可以轻松开启</strong></p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>oauth2-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="token-更换" tabindex="-1">Token 更换 <a class="header-anchor" href="#token-更换" aria-label="Permalink to &quot;Token 更换&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>当下主流开源项目默认采用 JWT 作为令牌生成器,但是对于无状态的令牌来说加上 <code>Redis</code> 做状态管理违背了 <code>JWT</code> 的设计初衷，对Redis Key 也不够友好，那么就可以考虑使用自定义的 TOKEN 策略了，默认实现了以 UUID 生成方式</strong></p>
<h3 id="开启配置" tabindex="-1">开启配置 <a class="header-anchor" href="#开启配置" aria-label="Permalink to &quot;开启配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>当你开启自定义 TOKEN 后,需要将 <code>oauth2_registered_client.token_settings</code> 内容修改一下</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">############## Sa-Token 配置 (文档: https://sa-token.cc) ##############</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">sa-token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 名称（同时也是 cookie 名称）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #  token-name: wp-token</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Authorization</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Bearer</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 有效期（单位：秒） 默认30天，-1 代表永久有效</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 最低活跃频率（单位：秒），如果 token 超过此时间没有访问系统就会被冻结，默认-1 代表不限制，永不冻结</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  active-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">-1</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否允许同一账号多地同时登录 （为 true 时允许一起登录, 为 false 时新登录挤掉旧登录）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-concurrent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 在多人登录同一账号时，是否共用一个 token （为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-share</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 风格（默认可取值：uuid、simple-uuid、random-32、random-64、random-128、tik）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">uuid</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否输出操作日志</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div></div>
<h2 id="服务端配置使用" tabindex="-1">服务端配置使用 <a class="header-anchor" href="#服务端配置使用" aria-label="Permalink to &quot;服务端配置使用&quot;">&ZeroWidthSpace;</a></h2>
<p>添加注解 <strong><code>@EnableOAuth2Server</code></strong> 标记当前项目为<strong>鉴权服务端</strong></p>
<h2 id="客户端配置使用" tabindex="-1">客户端配置使用 <a class="header-anchor" href="#客户端配置使用" aria-label="Permalink to &quot;客户端配置使用&quot;">&ZeroWidthSpace;</a></h2>
<p>添加注解 <strong><code>@EnableOAuth2Client</code></strong> 标记当前项目为<strong>资源客户端</strong></p>
]]></description>
            <content:encoded><![CDATA[<h1 id="token-管理" tabindex="-1">TOKEN 管理 <a class="header-anchor" href="#token-管理" aria-label="Permalink to &quot;TOKEN 管理&quot;">&ZeroWidthSpace;</a></h1>
<h2 id="导入核心包" tabindex="-1">导入核心包 <a class="header-anchor" href="#导入核心包" aria-label="Permalink to &quot;导入核心包&quot;">&ZeroWidthSpace;</a></h2>
<p>本 SDK 基于 <code>sa-token</code>提供的最新SDK <code>sa-token-spring-boot3-starter:1.39.0</code> 集成封装,简化开发者使用</p>
<p><strong>即便默认没提供 <code>OAuth2.0</code> 授权协议，但是采用 <code>sa-token</code> 后可以轻松开启</strong></p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>oauth2-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="token-更换" tabindex="-1">Token 更换 <a class="header-anchor" href="#token-更换" aria-label="Permalink to &quot;Token 更换&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>当下主流开源项目默认采用 JWT 作为令牌生成器,但是对于无状态的令牌来说加上 <code>Redis</code> 做状态管理违背了 <code>JWT</code> 的设计初衷，对Redis Key 也不够友好，那么就可以考虑使用自定义的 TOKEN 策略了，默认实现了以 UUID 生成方式</strong></p>
<h3 id="开启配置" tabindex="-1">开启配置 <a class="header-anchor" href="#开启配置" aria-label="Permalink to &quot;开启配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="warning custom-block"><p class="custom-block-title">注意</p>
<p>当你开启自定义 TOKEN 后,需要将 <code>oauth2_registered_client.token_settings</code> 内容修改一下</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">############## Sa-Token 配置 (文档: https://sa-token.cc) ##############</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">sa-token</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 名称（同时也是 cookie 名称）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  #  token-name: wp-token</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Authorization</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-prefix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">Bearer</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 有效期（单位：秒） 默认30天，-1 代表永久有效</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">86400</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 最低活跃频率（单位：秒），如果 token 超过此时间没有访问系统就会被冻结，默认-1 代表不限制，永不冻结</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  active-timeout</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">-1</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否允许同一账号多地同时登录 （为 true 时允许一起登录, 为 false 时新登录挤掉旧登录）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-concurrent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 在多人登录同一账号时，是否共用一个 token （为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-share</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # token 风格（默认可取值：uuid、simple-uuid、random-32、random-64、random-128、tik）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  token-style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">uuid</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 是否输出操作日志</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  is-log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div></div>
<h2 id="服务端配置使用" tabindex="-1">服务端配置使用 <a class="header-anchor" href="#服务端配置使用" aria-label="Permalink to &quot;服务端配置使用&quot;">&ZeroWidthSpace;</a></h2>
<p>添加注解 <strong><code>@EnableOAuth2Server</code></strong> 标记当前项目为<strong>鉴权服务端</strong></p>
<h2 id="客户端配置使用" tabindex="-1">客户端配置使用 <a class="header-anchor" href="#客户端配置使用" aria-label="Permalink to &quot;客户端配置使用&quot;">&ZeroWidthSpace;</a></h2>
<p>添加注解 <strong><code>@EnableOAuth2Client</code></strong> 标记当前项目为<strong>资源客户端</strong></p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[租户配置 ]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/tenant.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/tenant.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="租户配置" tabindex="-1">租户配置 <a class="header-anchor" href="#租户配置" aria-label="Permalink to &quot;租户配置&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>支持市面上常见的租户隔离方案 <code>字段隔离</code>、<code>Schema</code>、<code>动态数据源</code> 及组合策略。</p>
</div>
<h2 id="添加依赖" tabindex="-1">添加依赖 <a class="header-anchor" href="#添加依赖" aria-label="Permalink to &quot;添加依赖&quot;">&ZeroWidthSpace;</a></h2>
<p>在 <code>pom.xml</code> 中添加 SDK 的依赖包,可以参考项目中的案例</p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>db-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>${lastVersion}&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="租户策略" tabindex="-1">租户策略 <a class="header-anchor" href="#租户策略" aria-label="Permalink to &quot;租户策略&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>/</th>
<th>字段隔离</th>
<th>数据源隔离</th>
<th>是否支持</th>
<th>是否开源</th>
</tr>
</thead>
<tbody>
<tr>
<td>简易</td>
<td>简单</td>
<td>复杂</td>
<td>支持</td>
<td>开源</td>
</tr>
<tr>
<td>事务</td>
<td>简单</td>
<td>复杂</td>
<td>支持</td>
<td>开源</td>
</tr>
<tr>
<td>规模</td>
<td>租户年订单量低于200W</td>
<td>租户年订单量高于200W</td>
<td>支持</td>
<td>开源</td>
</tr>
<tr>
<td>优点</td>
<td>方便统计数据和排查问题</td>
<td>平台想对所有租户数据做统计相对麻烦</td>
<td>具体业务具体实现</td>
<td>开源</td>
</tr>
</tbody>
</table>
<h3 id="字段隔离" tabindex="-1">字段隔离 <a class="header-anchor" href="#字段隔离" aria-label="Permalink to &quot;字段隔离&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>字段隔离：在需要对租户数据隔离的SQL添加租户字段进行数据过滤</strong></p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 表示字段隔离</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.type=column</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 表示需要进行租户数据隔离的表名(逗号分隔)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.include-tables=t_user,t_file</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 字段隔离需要关掉动态AOP</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.aop.enabled=false</span></span></code></pre>
</div><h3 id="动态数据源" tabindex="-1">动态数据源 <a class="header-anchor" href="#动态数据源" aria-label="Permalink to &quot;动态数据源&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>数据源隔离：没个租户独立数据源，使用 <code>Spring Cloud Bus</code> 动态切换分配数据源，项目初始化也会加载所有数据源</strong></p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 表示数据源隔离</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.type=datasource</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 非 iam 模块都配置成 feign 即可</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.strategy=feign</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.db-notify=redis</span></span></code></pre>
</div><blockquote>
<p>默认数据源配置</p>
</blockquote>
<div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.primary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">master</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.strict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.pool</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">HikariDataSourcePool</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.driver</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">com.mysql.cj.jdbc.Driver</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">jdbc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">//localhost:3306/wemirr-platform?useUnicode=true&#x26;characterEncoding=utf8&#x26;allowMultiQueries=true&#x26;serverTimezone=GMT%2B8&#x26;useSSL=false&#x26;allowPublicKeyRetrieval=true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">com.zaxxer.hikari.HikariDataSource</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.username</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">root</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">pool</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">size</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">15</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lifetime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1800000</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.connection</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">timeout</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30000</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.min</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">idle</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.is</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">auto</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">commit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="注意事项" tabindex="-1">注意事项 <a class="header-anchor" href="#注意事项" aria-label="Permalink to &quot;注意事项&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">注意事项</p>
<p>动态数据源初始化数据 SQL 脚本整理比较繁琐,开源项目迭代速度比较快,所以租户脚本不一定更新及时,所以请根据企业需求修改 <code>tenant_schema.sql</code></p>
<ul>
<li>只有 <strong>动态数据源隔离</strong> 存在这个问题</li>
</ul>
<p><img src="/tenant_schema.png" alt="租户初始化脚本"></p>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="租户配置" tabindex="-1">租户配置 <a class="header-anchor" href="#租户配置" aria-label="Permalink to &quot;租户配置&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>支持市面上常见的租户隔离方案 <code>字段隔离</code>、<code>Schema</code>、<code>动态数据源</code> 及组合策略。</p>
</div>
<h2 id="添加依赖" tabindex="-1">添加依赖 <a class="header-anchor" href="#添加依赖" aria-label="Permalink to &quot;添加依赖&quot;">&ZeroWidthSpace;</a></h2>
<p>在 <code>pom.xml</code> 中添加 SDK 的依赖包,可以参考项目中的案例</p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>com.wemirr.framework&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">groupId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>db-spring-boot-starter&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">artifactId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>${lastVersion}&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">version</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">dependency</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="租户策略" tabindex="-1">租户策略 <a class="header-anchor" href="#租户策略" aria-label="Permalink to &quot;租户策略&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>/</th>
<th>字段隔离</th>
<th>数据源隔离</th>
<th>是否支持</th>
<th>是否开源</th>
</tr>
</thead>
<tbody>
<tr>
<td>简易</td>
<td>简单</td>
<td>复杂</td>
<td>支持</td>
<td>开源</td>
</tr>
<tr>
<td>事务</td>
<td>简单</td>
<td>复杂</td>
<td>支持</td>
<td>开源</td>
</tr>
<tr>
<td>规模</td>
<td>租户年订单量低于200W</td>
<td>租户年订单量高于200W</td>
<td>支持</td>
<td>开源</td>
</tr>
<tr>
<td>优点</td>
<td>方便统计数据和排查问题</td>
<td>平台想对所有租户数据做统计相对麻烦</td>
<td>具体业务具体实现</td>
<td>开源</td>
</tr>
</tbody>
</table>
<h3 id="字段隔离" tabindex="-1">字段隔离 <a class="header-anchor" href="#字段隔离" aria-label="Permalink to &quot;字段隔离&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>字段隔离：在需要对租户数据隔离的SQL添加租户字段进行数据过滤</strong></p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 表示字段隔离</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.type=column</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 表示需要进行租户数据隔离的表名(逗号分隔)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.include-tables=t_user,t_file</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 字段隔离需要关掉动态AOP</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.aop.enabled=false</span></span></code></pre>
</div><h3 id="动态数据源" tabindex="-1">动态数据源 <a class="header-anchor" href="#动态数据源" aria-label="Permalink to &quot;动态数据源&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>数据源隔离：没个租户独立数据源，使用 <code>Spring Cloud Bus</code> 动态切换分配数据源，项目初始化也会加载所有数据源</strong></p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 表示数据源隔离</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.type=datasource</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 非 iam 模块都配置成 feign 即可</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.strategy=feign</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">extend.mybatis-plus.multi-tenant.db-notify=redis</span></span></code></pre>
</div><blockquote>
<p>默认数据源配置</p>
</blockquote>
<div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.primary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">master</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.strict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.pool</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">HikariDataSourcePool</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.driver</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">com.mysql.cj.jdbc.Driver</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">jdbc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mysql</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">//localhost:3306/wemirr-platform?useUnicode=true&#x26;characterEncoding=utf8&#x26;allowMultiQueries=true&#x26;serverTimezone=GMT%2B8&#x26;useSSL=false&#x26;allowPublicKeyRetrieval=true</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">com.zaxxer.hikari.HikariDataSource</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.username</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">root</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.datasource.master.password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">pool</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">size</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">15</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.max</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lifetime</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1800000</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.connection</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">timeout</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30000</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.min</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">idle</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">spring.datasource.dynamic.hikari.is</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">auto</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">commit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span></code></pre>
</div><h2 id="注意事项" tabindex="-1">注意事项 <a class="header-anchor" href="#注意事项" aria-label="Permalink to &quot;注意事项&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">注意事项</p>
<p>动态数据源初始化数据 SQL 脚本整理比较繁琐,开源项目迭代速度比较快,所以租户脚本不一定更新及时,所以请根据企业需求修改 <code>tenant_schema.sql</code></p>
<ul>
<li>只有 <strong>动态数据源隔离</strong> 存在这个问题</li>
</ul>
<p><img src="/tenant_schema.png" alt="租户初始化脚本"></p>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[无题]]></title>
            <link>https://docs.battcn.com/zh/guide/packages/utils/intro.html</link>
            <guid>https://docs.battcn.com/zh/guide/packages/utils/intro.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h3 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h3>
<p>工具函数</p>
]]></description>
            <content:encoded><![CDATA[<h3 id="介绍" tabindex="-1">介绍 <a class="header-anchor" href="#介绍" aria-label="Permalink to &quot;介绍&quot;">&ZeroWidthSpace;</a></h3>
<p>工具函数</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[核心概念 ]]></title>
            <link>https://docs.battcn.com/zh/guide/quickstart/concept.html</link>
            <guid>https://docs.battcn.com/zh/guide/quickstart/concept.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="核心概念" tabindex="-1">核心概念 <a class="header-anchor" href="#核心概念" aria-label="Permalink to &quot;核心概念&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>理解 SaaS、多租户、微服务等核心概念，为后续深入学习打下基础</p>
</div>
<h2 id="什么是-saas" tabindex="-1">什么是 SaaS？ <a class="header-anchor" href="#什么是-saas" aria-label="Permalink to &quot;什么是 SaaS？&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>SaaS（Software as a Service）</strong> 即软件即服务，是一种通过互联网提供软件的模式。</p>
<h3 id="传统软件-vs-saas" tabindex="-1">传统软件 vs SaaS <a class="header-anchor" href="#传统软件-vs-saas" aria-label="Permalink to &quot;传统软件 vs SaaS&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>对比项</th>
<th>传统软件</th>
<th>SaaS 软件</th>
</tr>
</thead>
<tbody>
<tr>
<td>部署方式</td>
<td>本地安装</td>
<td>云端部署</td>
</tr>
<tr>
<td>维护成本</td>
<td>用户自行维护</td>
<td>服务商维护</td>
</tr>
<tr>
<td>更新升级</td>
<td>手动升级</td>
<td>自动更新</td>
</tr>
<tr>
<td>付费模式</td>
<td>一次性购买</td>
<td>订阅制</td>
</tr>
<tr>
<td>访问方式</td>
<td>限定设备</td>
<td>任意设备</td>
</tr>
</tbody>
</table>
<h3 id="saas-的优势" tabindex="-1">SaaS 的优势 <a class="header-anchor" href="#saas-的优势" aria-label="Permalink to &quot;SaaS 的优势&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                    SaaS 架构优势                         │</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>│  📱 多端访问     │ 支持 PC、手机、平板等设备            │</span></span>
<span class="line"><span>│  🔒 数据安全     │ 专业团队保障数据安全                 │</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;">&ZeroWidthSpace;</a></h2>
<p><strong>多租户（Multi-Tenant）</strong> 是 SaaS 的核心架构模式，让多个租户（客户）共享同一套应用程序，同时保证数据隔离。</p>
<h3 id="租户隔离策略" tabindex="-1">租户隔离策略 <a class="header-anchor" href="#租户隔离策略" aria-label="Permalink to &quot;租户隔离策略&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr Platform 支持三种租户隔离策略：</p>
<h4 id="_1-字段隔离-推荐入门使用" tabindex="-1">1. 字段隔离（推荐入门使用） <a class="header-anchor" href="#_1-字段隔离-推荐入门使用" aria-label="Permalink to &quot;1. 字段隔离（推荐入门使用）&quot;">&ZeroWidthSpace;</a></h4>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌────────────────────────────────────────┐</span></span>
<span class="line"><span>│            共享数据库                    │</span></span>
<span class="line"><span>├────────────────────────────────────────┤</span></span>
<span class="line"><span>│  ID  │ tenant_id │  name  │   data    │</span></span>
<span class="line"><span>│  1   │    A      │  张三  │   ...     │</span></span>
<span class="line"><span>│  2   │    A      │  李四  │   ...     │</span></span>
<span class="line"><span>│  3   │    B      │  王五  │   ...     │</span></span>
<span class="line"><span>│  4   │    B      │  赵六  │   ...     │</span></span>
<span class="line"><span>└────────────────────────────────────────┘</span></span>
<span class="line"><span>通过 tenant_id 字段区分不同租户数据</span></span></code></pre>
</div><p><strong>优点：</strong> 简单、成本低、易维护<br>
<strong>缺点：</strong> 大租户可能影响小租户性能<br>
<strong>适用：</strong> 中小型租户，年订单量 &lt; 200W</p>
<h4 id="_2-schema-隔离" tabindex="-1">2. Schema 隔离 <a class="header-anchor" href="#_2-schema-隔离" aria-label="Permalink to &quot;2. Schema 隔离&quot;">&ZeroWidthSpace;</a></h4>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────┐</span></span>
<span class="line"><span>│              共享数据库实例               │</span></span>
<span class="line"><span>├─────────────┬─────────────┬─────────────┤</span></span>
<span class="line"><span>│  Schema_A   │  Schema_B   │  Schema_C   │</span></span>
<span class="line"><span>│  ├─ users   │  ├─ users   │  ├─ users   │</span></span>
<span class="line"><span>│  ├─ orders  │  ├─ orders  │  ├─ orders  │</span></span>
<span class="line"><span>│  └─ ...     │  └─ ...     │  └─ ...     │</span></span>
<span class="line"><span>└─────────────┴─────────────┴─────────────┘</span></span>
<span class="line"><span>每个租户独立 Schema，物理隔离表</span></span></code></pre>
</div><p><strong>优点：</strong> 隔离性好、便于备份恢复<br>
<strong>缺点：</strong> 租户数量受限、跨租户查询复杂<br>
<strong>适用：</strong> 中型租户，需要较好隔离性</p>
<h4 id="_3-数据源隔离" tabindex="-1">3. 数据源隔离 <a class="header-anchor" href="#_3-数据源隔离" aria-label="Permalink to &quot;3. 数据源隔离&quot;">&ZeroWidthSpace;</a></h4>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐  ┌─────────────┐  ┌─────────────┐</span></span>
<span class="line"><span>│  Database_A │  │  Database_B │  │  Database_C │</span></span>
<span class="line"><span>│  (租户 A)   │  │  (租户 B)   │  │  (租户 C)   │</span></span>
<span class="line"><span>│  ├─ users   │  │  ├─ users   │  │  ├─ users   │</span></span>
<span class="line"><span>│  ├─ orders  │  │  ├─ orders  │  │  ├─ orders  │</span></span>
<span class="line"><span>│  └─ ...     │  │  └─ ...     │  │  └─ ...     │</span></span>
<span class="line"><span>└─────────────┘  └─────────────┘  └─────────────┘</span></span>
<span class="line"><span>每个租户完全独立的数据库</span></span></code></pre>
</div><p><strong>优点：</strong> 完全隔离、性能最优、便于定制<br>
<strong>缺点：</strong> 成本高、运维复杂<br>
<strong>适用：</strong> 大型租户，年订单量 &gt; 200W</p>
<h3 id="如何选择" tabindex="-1">如何选择？ <a class="header-anchor" href="#如何选择" aria-label="Permalink to &quot;如何选择？&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>场景</th>
<th>推荐方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>初创企业，快速验证</td>
<td>字段隔离</td>
</tr>
<tr>
<td>中型企业，平衡成本与隔离</td>
<td>Schema 隔离</td>
</tr>
<tr>
<td>大型企业，高安全要求</td>
<td>数据源隔离</td>
</tr>
<tr>
<td>混合场景</td>
<td>组合策略</td>
</tr>
</tbody>
</table>
<h2 id="微服务架构" tabindex="-1">微服务架构 <a class="header-anchor" href="#微服务架构" aria-label="Permalink to &quot;微服务架构&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 采用 Spring Cloud 微服务架构：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>                    ┌─────────────┐</span></span>
<span class="line"><span>                    │   Nginx     │</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>                    │   Gateway   │</span></span>
<span class="line"><span>                    │  (API网关)   │</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>    │   IAM   │      │  Suite  │      │ Plugin  │</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>         ┌────▼────┐  ┌────▼────┐  ┌────▼────┐</span></span>
<span class="line"><span>         │  Nacos  │  │  Redis  │  │  MySQL  │</span></span>
<span class="line"><span>         │(注册中心)│  │ (缓存)  │  │(数据库) │</span></span>
<span class="line"><span>         └─────────┘  └─────────┘  └─────────┘</span></span></code></pre>
</div><h3 id="核心服务说明" tabindex="-1">核心服务说明 <a class="header-anchor" href="#核心服务说明" aria-label="Permalink to &quot;核心服务说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>服务</th>
<th>端口</th>
<th>职责</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Gateway</strong></td>
<td>9000</td>
<td>统一入口、路由转发、限流、鉴权</td>
</tr>
<tr>
<td><strong>IAM</strong></td>
<td>5001</td>
<td>用户认证、授权、租户管理</td>
</tr>
<tr>
<td><strong>Suite</strong></td>
<td>5002</td>
<td>核心业务功能</td>
</tr>
<tr>
<td><strong>Plugin</strong></td>
<td>按需</td>
<td>可插拔的扩展模块</td>
</tr>
</tbody>
</table>
<h2 id="rbac-权限模型" tabindex="-1">RBAC 权限模型 <a class="header-anchor" href="#rbac-权限模型" aria-label="Permalink to &quot;RBAC 权限模型&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>RBAC（Role-Based Access Control）</strong> 基于角色的访问控制：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────┐      ┌─────────┐      ┌─────────┐</span></span>
<span class="line"><span>│  用户   │─────▶│  角色   │─────▶│  权限   │</span></span>
<span class="line"><span>│ (User)  │ 1:N  │ (Role)  │ N:M  │(Permission)│</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>                  │ (Menu)  │    │(Button) │    │ (Data)  │</span></span>
<span class="line"><span>                  └─────────┘    └─────────┘    └─────────┘</span></span></code></pre>
</div><h3 id="权限层级" tabindex="-1">权限层级 <a class="header-anchor" href="#权限层级" aria-label="Permalink to &quot;权限层级&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>菜单权限</strong> - 控制用户能看到哪些菜单</li>
<li><strong>按钮权限</strong> - 控制用户能操作哪些按钮</li>
<li><strong>数据权限</strong> - 控制用户能访问哪些数据（本人、本部门、全部等）</li>
</ul>
<h2 id="技术栈一览" tabindex="-1">技术栈一览 <a class="header-anchor" href="#技术栈一览" aria-label="Permalink to &quot;技术栈一览&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="后端技术栈" tabindex="-1">后端技术栈 <a class="header-anchor" href="#后端技术栈" aria-label="Permalink to &quot;后端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK</td>
<td>21</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>java.version</code>）</td>
</tr>
<tr>
<td>Spring Boot</td>
<td>3.5.7</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>spring-boot.version</code>）</td>
</tr>
<tr>
<td>Spring Cloud</td>
<td>2025.0.0</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>spring-cloud.version</code>）</td>
</tr>
<tr>
<td>Spring Cloud Alibaba</td>
<td>2025.0.0.0</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>spring-cloud-alibaba.version</code>）</td>
</tr>
<tr>
<td>Sa-Token</td>
<td>1.44.0</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>sa-token.version</code>）</td>
</tr>
<tr>
<td>MyBatis-Plus</td>
<td>3.5.15</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>mybatis-plus.version</code>）</td>
</tr>
<tr>
<td>MySQL（运行环境）</td>
<td>8.0.19</td>
<td>来自 <code>附件/docker/docker-compose.yml</code>（<code>mysql:8.0.19</code>）</td>
</tr>
<tr>
<td>Redis（运行环境）</td>
<td>5.0</td>
<td>来自 <code>附件/docker/docker-compose.yml</code>（<code>redis:5.0</code>）</td>
</tr>
<tr>
<td>Nacos（运行环境）</td>
<td>2.4.2</td>
<td>来自 <code>附件/docker/docker-compose.yml</code>（<code>nacos/nacos-server:2.4.2</code>）</td>
</tr>
</tbody>
</table>
<h3 id="前端技术栈" tabindex="-1">前端技术栈 <a class="header-anchor" href="#前端技术栈" aria-label="Permalink to &quot;前端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vben Admin（Monorepo）</td>
<td>5.5.9</td>
<td>来自 <code>wemirr-platform-ui/package.json</code>（<code>version</code>）</td>
</tr>
<tr>
<td>Vue</td>
<td>3.5.24</td>
<td>来自 <code>wemirr-platform-ui/pnpm-lock.yaml</code>（catalog: <code>vue</code>）</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.9.3</td>
<td>来自 <code>wemirr-platform-ui/pnpm-lock.yaml</code>（catalog: <code>typescript</code>）</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.2.6</td>
<td>来自 <code>wemirr-platform-ui/pnpm-workspace.yaml</code> + <code>pnpm-lock.yaml</code>（catalog: <code>ant-design-vue</code>）</td>
</tr>
<tr>
<td>Vite</td>
<td>7.2.2</td>
<td>来自 <code>wemirr-platform-ui/pnpm-workspace.yaml</code> + <code>pnpm-lock.yaml</code>（catalog: <code>vite</code>）</td>
</tr>
<tr>
<td>Pinia</td>
<td>3.0.3</td>
<td>来自 <code>wemirr-platform-ui/pnpm-workspace.yaml</code> + <code>pnpm-lock.yaml</code>（catalog: <code>pinia</code>）</td>
</tr>
</tbody>
</table>
<div class="tip custom-block"><p class="custom-block-title">版本说明</p>
<p>为避免“文档版本”和“项目实际依赖”不一致，上述版本来自本地源码：</p>
<ul>
<li>后端：<code>wemirr-platform-dependencies/pom.xml</code></li>
<li>前端：<code>wemirr-platform-ui/pnpm-workspace.yaml</code> 与 <code>wemirr-platform-ui/pnpm-lock.yaml</code></li>
</ul>
</div>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/env/install.html">环境安装</a> - 搭建开发环境</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 学习容器化部署</li>
<li><a href="/zh/guide/packages/tenant.html">租户配置</a> - 深入理解多租户</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="核心概念" tabindex="-1">核心概念 <a class="header-anchor" href="#核心概念" aria-label="Permalink to &quot;核心概念&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>理解 SaaS、多租户、微服务等核心概念，为后续深入学习打下基础</p>
</div>
<h2 id="什么是-saas" tabindex="-1">什么是 SaaS？ <a class="header-anchor" href="#什么是-saas" aria-label="Permalink to &quot;什么是 SaaS？&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>SaaS（Software as a Service）</strong> 即软件即服务，是一种通过互联网提供软件的模式。</p>
<h3 id="传统软件-vs-saas" tabindex="-1">传统软件 vs SaaS <a class="header-anchor" href="#传统软件-vs-saas" aria-label="Permalink to &quot;传统软件 vs SaaS&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>对比项</th>
<th>传统软件</th>
<th>SaaS 软件</th>
</tr>
</thead>
<tbody>
<tr>
<td>部署方式</td>
<td>本地安装</td>
<td>云端部署</td>
</tr>
<tr>
<td>维护成本</td>
<td>用户自行维护</td>
<td>服务商维护</td>
</tr>
<tr>
<td>更新升级</td>
<td>手动升级</td>
<td>自动更新</td>
</tr>
<tr>
<td>付费模式</td>
<td>一次性购买</td>
<td>订阅制</td>
</tr>
<tr>
<td>访问方式</td>
<td>限定设备</td>
<td>任意设备</td>
</tr>
</tbody>
</table>
<h3 id="saas-的优势" tabindex="-1">SaaS 的优势 <a class="header-anchor" href="#saas-的优势" aria-label="Permalink to &quot;SaaS 的优势&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                    SaaS 架构优势                         │</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>│  📱 多端访问     │ 支持 PC、手机、平板等设备            │</span></span>
<span class="line"><span>│  🔒 数据安全     │ 专业团队保障数据安全                 │</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;">&ZeroWidthSpace;</a></h2>
<p><strong>多租户（Multi-Tenant）</strong> 是 SaaS 的核心架构模式，让多个租户（客户）共享同一套应用程序，同时保证数据隔离。</p>
<h3 id="租户隔离策略" tabindex="-1">租户隔离策略 <a class="header-anchor" href="#租户隔离策略" aria-label="Permalink to &quot;租户隔离策略&quot;">&ZeroWidthSpace;</a></h3>
<p>Wemirr Platform 支持三种租户隔离策略：</p>
<h4 id="_1-字段隔离-推荐入门使用" tabindex="-1">1. 字段隔离（推荐入门使用） <a class="header-anchor" href="#_1-字段隔离-推荐入门使用" aria-label="Permalink to &quot;1. 字段隔离（推荐入门使用）&quot;">&ZeroWidthSpace;</a></h4>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌────────────────────────────────────────┐</span></span>
<span class="line"><span>│            共享数据库                    │</span></span>
<span class="line"><span>├────────────────────────────────────────┤</span></span>
<span class="line"><span>│  ID  │ tenant_id │  name  │   data    │</span></span>
<span class="line"><span>│  1   │    A      │  张三  │   ...     │</span></span>
<span class="line"><span>│  2   │    A      │  李四  │   ...     │</span></span>
<span class="line"><span>│  3   │    B      │  王五  │   ...     │</span></span>
<span class="line"><span>│  4   │    B      │  赵六  │   ...     │</span></span>
<span class="line"><span>└────────────────────────────────────────┘</span></span>
<span class="line"><span>通过 tenant_id 字段区分不同租户数据</span></span></code></pre>
</div><p><strong>优点：</strong> 简单、成本低、易维护<br>
<strong>缺点：</strong> 大租户可能影响小租户性能<br>
<strong>适用：</strong> 中小型租户，年订单量 &lt; 200W</p>
<h4 id="_2-schema-隔离" tabindex="-1">2. Schema 隔离 <a class="header-anchor" href="#_2-schema-隔离" aria-label="Permalink to &quot;2. Schema 隔离&quot;">&ZeroWidthSpace;</a></h4>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────┐</span></span>
<span class="line"><span>│              共享数据库实例               │</span></span>
<span class="line"><span>├─────────────┬─────────────┬─────────────┤</span></span>
<span class="line"><span>│  Schema_A   │  Schema_B   │  Schema_C   │</span></span>
<span class="line"><span>│  ├─ users   │  ├─ users   │  ├─ users   │</span></span>
<span class="line"><span>│  ├─ orders  │  ├─ orders  │  ├─ orders  │</span></span>
<span class="line"><span>│  └─ ...     │  └─ ...     │  └─ ...     │</span></span>
<span class="line"><span>└─────────────┴─────────────┴─────────────┘</span></span>
<span class="line"><span>每个租户独立 Schema，物理隔离表</span></span></code></pre>
</div><p><strong>优点：</strong> 隔离性好、便于备份恢复<br>
<strong>缺点：</strong> 租户数量受限、跨租户查询复杂<br>
<strong>适用：</strong> 中型租户，需要较好隔离性</p>
<h4 id="_3-数据源隔离" tabindex="-1">3. 数据源隔离 <a class="header-anchor" href="#_3-数据源隔离" aria-label="Permalink to &quot;3. 数据源隔离&quot;">&ZeroWidthSpace;</a></h4>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐  ┌─────────────┐  ┌─────────────┐</span></span>
<span class="line"><span>│  Database_A │  │  Database_B │  │  Database_C │</span></span>
<span class="line"><span>│  (租户 A)   │  │  (租户 B)   │  │  (租户 C)   │</span></span>
<span class="line"><span>│  ├─ users   │  │  ├─ users   │  │  ├─ users   │</span></span>
<span class="line"><span>│  ├─ orders  │  │  ├─ orders  │  │  ├─ orders  │</span></span>
<span class="line"><span>│  └─ ...     │  │  └─ ...     │  │  └─ ...     │</span></span>
<span class="line"><span>└─────────────┘  └─────────────┘  └─────────────┘</span></span>
<span class="line"><span>每个租户完全独立的数据库</span></span></code></pre>
</div><p><strong>优点：</strong> 完全隔离、性能最优、便于定制<br>
<strong>缺点：</strong> 成本高、运维复杂<br>
<strong>适用：</strong> 大型租户，年订单量 &gt; 200W</p>
<h3 id="如何选择" tabindex="-1">如何选择？ <a class="header-anchor" href="#如何选择" aria-label="Permalink to &quot;如何选择？&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>场景</th>
<th>推荐方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>初创企业，快速验证</td>
<td>字段隔离</td>
</tr>
<tr>
<td>中型企业，平衡成本与隔离</td>
<td>Schema 隔离</td>
</tr>
<tr>
<td>大型企业，高安全要求</td>
<td>数据源隔离</td>
</tr>
<tr>
<td>混合场景</td>
<td>组合策略</td>
</tr>
</tbody>
</table>
<h2 id="微服务架构" tabindex="-1">微服务架构 <a class="header-anchor" href="#微服务架构" aria-label="Permalink to &quot;微服务架构&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 采用 Spring Cloud 微服务架构：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>                    ┌─────────────┐</span></span>
<span class="line"><span>                    │   Nginx     │</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>                    │   Gateway   │</span></span>
<span class="line"><span>                    │  (API网关)   │</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>    │   IAM   │      │  Suite  │      │ Plugin  │</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>         ┌────▼────┐  ┌────▼────┐  ┌────▼────┐</span></span>
<span class="line"><span>         │  Nacos  │  │  Redis  │  │  MySQL  │</span></span>
<span class="line"><span>         │(注册中心)│  │ (缓存)  │  │(数据库) │</span></span>
<span class="line"><span>         └─────────┘  └─────────┘  └─────────┘</span></span></code></pre>
</div><h3 id="核心服务说明" tabindex="-1">核心服务说明 <a class="header-anchor" href="#核心服务说明" aria-label="Permalink to &quot;核心服务说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>服务</th>
<th>端口</th>
<th>职责</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Gateway</strong></td>
<td>9000</td>
<td>统一入口、路由转发、限流、鉴权</td>
</tr>
<tr>
<td><strong>IAM</strong></td>
<td>5001</td>
<td>用户认证、授权、租户管理</td>
</tr>
<tr>
<td><strong>Suite</strong></td>
<td>5002</td>
<td>核心业务功能</td>
</tr>
<tr>
<td><strong>Plugin</strong></td>
<td>按需</td>
<td>可插拔的扩展模块</td>
</tr>
</tbody>
</table>
<h2 id="rbac-权限模型" tabindex="-1">RBAC 权限模型 <a class="header-anchor" href="#rbac-权限模型" aria-label="Permalink to &quot;RBAC 权限模型&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>RBAC（Role-Based Access Control）</strong> 基于角色的访问控制：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────┐      ┌─────────┐      ┌─────────┐</span></span>
<span class="line"><span>│  用户   │─────▶│  角色   │─────▶│  权限   │</span></span>
<span class="line"><span>│ (User)  │ 1:N  │ (Role)  │ N:M  │(Permission)│</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>                  │ (Menu)  │    │(Button) │    │ (Data)  │</span></span>
<span class="line"><span>                  └─────────┘    └─────────┘    └─────────┘</span></span></code></pre>
</div><h3 id="权限层级" tabindex="-1">权限层级 <a class="header-anchor" href="#权限层级" aria-label="Permalink to &quot;权限层级&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>菜单权限</strong> - 控制用户能看到哪些菜单</li>
<li><strong>按钮权限</strong> - 控制用户能操作哪些按钮</li>
<li><strong>数据权限</strong> - 控制用户能访问哪些数据（本人、本部门、全部等）</li>
</ul>
<h2 id="技术栈一览" tabindex="-1">技术栈一览 <a class="header-anchor" href="#技术栈一览" aria-label="Permalink to &quot;技术栈一览&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="后端技术栈" tabindex="-1">后端技术栈 <a class="header-anchor" href="#后端技术栈" aria-label="Permalink to &quot;后端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK</td>
<td>21</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>java.version</code>）</td>
</tr>
<tr>
<td>Spring Boot</td>
<td>3.5.7</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>spring-boot.version</code>）</td>
</tr>
<tr>
<td>Spring Cloud</td>
<td>2025.0.0</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>spring-cloud.version</code>）</td>
</tr>
<tr>
<td>Spring Cloud Alibaba</td>
<td>2025.0.0.0</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>spring-cloud-alibaba.version</code>）</td>
</tr>
<tr>
<td>Sa-Token</td>
<td>1.44.0</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>sa-token.version</code>）</td>
</tr>
<tr>
<td>MyBatis-Plus</td>
<td>3.5.15</td>
<td>来自 <code>wemirr-platform-dependencies/pom.xml</code>（<code>mybatis-plus.version</code>）</td>
</tr>
<tr>
<td>MySQL（运行环境）</td>
<td>8.0.19</td>
<td>来自 <code>附件/docker/docker-compose.yml</code>（<code>mysql:8.0.19</code>）</td>
</tr>
<tr>
<td>Redis（运行环境）</td>
<td>5.0</td>
<td>来自 <code>附件/docker/docker-compose.yml</code>（<code>redis:5.0</code>）</td>
</tr>
<tr>
<td>Nacos（运行环境）</td>
<td>2.4.2</td>
<td>来自 <code>附件/docker/docker-compose.yml</code>（<code>nacos/nacos-server:2.4.2</code>）</td>
</tr>
</tbody>
</table>
<h3 id="前端技术栈" tabindex="-1">前端技术栈 <a class="header-anchor" href="#前端技术栈" aria-label="Permalink to &quot;前端技术栈&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>技术</th>
<th>版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Vben Admin（Monorepo）</td>
<td>5.5.9</td>
<td>来自 <code>wemirr-platform-ui/package.json</code>（<code>version</code>）</td>
</tr>
<tr>
<td>Vue</td>
<td>3.5.24</td>
<td>来自 <code>wemirr-platform-ui/pnpm-lock.yaml</code>（catalog: <code>vue</code>）</td>
</tr>
<tr>
<td>TypeScript</td>
<td>5.9.3</td>
<td>来自 <code>wemirr-platform-ui/pnpm-lock.yaml</code>（catalog: <code>typescript</code>）</td>
</tr>
<tr>
<td>Ant Design Vue</td>
<td>4.2.6</td>
<td>来自 <code>wemirr-platform-ui/pnpm-workspace.yaml</code> + <code>pnpm-lock.yaml</code>（catalog: <code>ant-design-vue</code>）</td>
</tr>
<tr>
<td>Vite</td>
<td>7.2.2</td>
<td>来自 <code>wemirr-platform-ui/pnpm-workspace.yaml</code> + <code>pnpm-lock.yaml</code>（catalog: <code>vite</code>）</td>
</tr>
<tr>
<td>Pinia</td>
<td>3.0.3</td>
<td>来自 <code>wemirr-platform-ui/pnpm-workspace.yaml</code> + <code>pnpm-lock.yaml</code>（catalog: <code>pinia</code>）</td>
</tr>
</tbody>
</table>
<div class="tip custom-block"><p class="custom-block-title">版本说明</p>
<p>为避免“文档版本”和“项目实际依赖”不一致，上述版本来自本地源码：</p>
<ul>
<li>后端：<code>wemirr-platform-dependencies/pom.xml</code></li>
<li>前端：<code>wemirr-platform-ui/pnpm-workspace.yaml</code> 与 <code>wemirr-platform-ui/pnpm-lock.yaml</code></li>
</ul>
</div>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/env/install.html">环境安装</a> - 搭建开发环境</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 学习容器化部署</li>
<li><a href="/zh/guide/packages/tenant.html">租户配置</a> - 深入理解多租户</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[新手常见问题 ]]></title>
            <link>https://docs.battcn.com/zh/guide/quickstart/faq.html</link>
            <guid>https://docs.battcn.com/zh/guide/quickstart/faq.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="新手常见问题" tabindex="-1">新手常见问题 <a class="header-anchor" href="#新手常见问题" aria-label="Permalink to &quot;新手常见问题&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>本页面收集了新手入门时最常遇到的问题和解决方案，包括一些&quot;看起来很蠢但确实会遇到&quot;的问题。</p>
</div>
<hr>
<h2 id="概念扫盲" tabindex="-1">概念扫盲 <a class="header-anchor" href="#概念扫盲" aria-label="Permalink to &quot;概念扫盲&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-这个项目为什么有这么多模块-我该启动哪个" tabindex="-1">Q: 这个项目为什么有这么多模块？我该启动哪个？ <a class="header-anchor" href="#q-这个项目为什么有这么多模块-我该启动哪个" aria-label="Permalink to &quot;Q: 这个项目为什么有这么多模块？我该启动哪个？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 这是微服务架构，不同服务负责不同功能：</p>
<table tabindex="0">
<thead>
<tr>
<th>服务</th>
<th>作用</th>
<th>必须启动？</th>
</tr>
</thead>
<tbody>
<tr>
<td>Gateway</td>
<td>网关，所有请求的入口</td>
<td>✅ 必须</td>
</tr>
<tr>
<td>IAM</td>
<td>认证授权，负责登录</td>
<td>✅ 必须</td>
</tr>
<tr>
<td>Suite</td>
<td>核心业务</td>
<td>✅ 必须</td>
</tr>
<tr>
<td>Workflow</td>
<td>工作流（可选）</td>
<td>❌ 按需</td>
</tr>
<tr>
<td>TMS</td>
<td>运输管理（可选）</td>
<td>❌ 按需</td>
</tr>
<tr>
<td>WMS</td>
<td>仓储管理（可选）</td>
<td>❌ 按需</td>
</tr>
</tbody>
</table>
<p><strong>最小启动</strong>：Gateway + IAM + Suite</p>
<h3 id="q-什么是-nacos-为什么要装这个" tabindex="-1">Q: 什么是 Nacos？为什么要装这个？ <a class="header-anchor" href="#q-什么是-nacos-为什么要装这个" aria-label="Permalink to &quot;Q: 什么是 Nacos？为什么要装这个？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Nacos 是配置中心 + 服务注册中心：</p>
<ul>
<li><strong>配置中心</strong>：数据库密码、Redis 地址等配置统一放 Nacos，不用改代码</li>
<li><strong>服务注册</strong>：各个微服务启动后会注册到 Nacos，网关才知道怎么转发请求</li>
</ul>
<p>不装 Nacos = 项目跑不起来。</p>
<h3 id="q-什么是租户-多租户是什么意思" tabindex="-1">Q: 什么是租户？多租户是什么意思？ <a class="header-anchor" href="#q-什么是租户-多租户是什么意思" aria-label="Permalink to &quot;Q: 什么是租户？多租户是什么意思？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 简单理解：</p>
<ul>
<li><strong>租户</strong> = 公司/组织</li>
<li><strong>多租户</strong> = 一套系统给多个公司用，数据互相隔离</li>
</ul>
<p>比如 SaaS 软件，A公司和B公司都用这套系统，但 A 看不到 B 的数据。</p>
<p>登录时填的「租户编码」就是区分你属于哪个公司：</p>
<ul>
<li><code>0000</code> = 平台管理员（超级管理员，能管所有租户）</li>
<li><code>8888</code> = 示例租户（普通租户）</li>
</ul>
<h3 id="q-前后端分离是什么意思" tabindex="-1">Q: 前后端分离是什么意思？ <a class="header-anchor" href="#q-前后端分离是什么意思" aria-label="Permalink to &quot;Q: 前后端分离是什么意思？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ul>
<li><strong>后端</strong>（wemirr-platform）：提供 API 接口，返回 JSON 数据</li>
<li><strong>前端</strong>（wemirr-platform-ui）：负责页面展示，调用后端接口获取数据</li>
</ul>
<p>两个项目独立运行，通过 HTTP 请求通信。</p>
<h3 id="q-token-是什么-为什么要-token" tabindex="-1">Q: Token 是什么？为什么要 Token？ <a class="header-anchor" href="#q-token-是什么-为什么要-token" aria-label="Permalink to &quot;Q: Token 是什么？为什么要 Token？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Token 是登录凭证，相当于&quot;门禁卡&quot;：</p>
<ol>
<li>登录成功 → 后端发一个 Token 给你</li>
<li>之后每次请求 → 带上这个 Token</li>
<li>后端验证 Token → 知道你是谁</li>
</ol>
<p>Token 过期了就需要重新登录。</p>
<hr>
<h2 id="工具使用" tabindex="-1">工具使用 <a class="header-anchor" href="#工具使用" aria-label="Permalink to &quot;工具使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-什么是-maven-我需要单独安装吗" tabindex="-1">Q: 什么是 Maven？我需要单独安装吗？ <a class="header-anchor" href="#q-什么是-maven-我需要单独安装吗" aria-label="Permalink to &quot;Q: 什么是 Maven？我需要单独安装吗？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Maven 是 Java 的包管理工具（类似前端的 npm）：</p>
<ul>
<li><strong>作用</strong>：自动下载项目依赖的 jar 包</li>
<li><strong>安装</strong>：IDEA 自带 Maven，不需要单独安装</li>
<li><strong>配置镜像</strong>：国内下载慢，需配置阿里云镜像（见下文）</li>
</ul>
<h3 id="q-idea-怎么打开这个项目" tabindex="-1">Q: IDEA 怎么打开这个项目？ <a class="header-anchor" href="#q-idea-怎么打开这个项目" aria-label="Permalink to &quot;Q: IDEA 怎么打开这个项目？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ol>
<li>File → Open → 选择 <code>wemirr-platform</code> 文件夹</li>
<li>等待右下角进度条走完（Maven 在下载依赖）</li>
<li>如果报红，等一会儿，或者点击右侧 Maven 图标 → 刷新按钮</li>
</ol>
<h3 id="q-pnpm-是什么-和-npm-有什么区别" tabindex="-1">Q: pnpm 是什么？和 npm 有什么区别？ <a class="header-anchor" href="#q-pnpm-是什么-和-npm-有什么区别" aria-label="Permalink to &quot;Q: pnpm 是什么？和 npm 有什么区别？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 都是前端包管理工具：</p>
<ul>
<li><strong>npm</strong>：Node.js 自带的，但比较慢</li>
<li><strong>pnpm</strong>：更快、更省空间的替代品</li>
</ul>
<p>本项目用 pnpm，安装命令：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span></code></pre>
</div><h3 id="q-我不会用-git-命令怎么办" tabindex="-1">Q: 我不会用 Git 命令怎么办？ <a class="header-anchor" href="#q-我不会用-git-命令怎么办" aria-label="Permalink to &quot;Q: 我不会用 Git 命令怎么办？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 最简单的方式：直接在 Gitee 页面下载 ZIP 压缩包</p>
<p>或者用图形化工具：</p>
<ul>
<li><strong>IDEA</strong>：自带 Git 功能，VCS → Get from Version Control</li>
<li><strong>SourceTree</strong>：免费的 Git 图形化客户端</li>
</ul>
<h3 id="q-docker-是什么-必须用吗" tabindex="-1">Q: Docker 是什么？必须用吗？ <a class="header-anchor" href="#q-docker-是什么-必须用吗" aria-label="Permalink to &quot;Q: Docker 是什么？必须用吗？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Docker 是容器技术，可以一键启动 MySQL、Redis、Nacos 等中间件。</p>
<p><strong>必须用吗？</strong> 不是必须，但强烈推荐：</p>
<ul>
<li>用 Docker：一条命令启动所有环境</li>
<li>不用 Docker：需要手动安装 MySQL、Redis、Nacos...（很麻烦）</li>
</ul>
<hr>
<h2 id="环境问题" tabindex="-1">环境问题 <a class="header-anchor" href="#环境问题" aria-label="Permalink to &quot;环境问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-docker-启动失败" tabindex="-1">Q: Docker 启动失败？ <a class="header-anchor" href="#q-docker-启动失败" aria-label="Permalink to &quot;Q: Docker 启动失败？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 常见原因及解决方案：</p>
<ol>
<li><strong>端口被占用</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:3306</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:6379</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:8848</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 杀掉占用进程或修改 docker-compose 端口映射</span></span></code></pre>
</div><ol start="2">
<li><strong>内存不足</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Docker Desktop 设置中增加内存限制（建议至少 4GB）</span></span></code></pre>
</div><ol start="3">
<li><strong>网络问题</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重新创建 Docker 网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><h3 id="q-maven-依赖下载失败" tabindex="-1">Q: Maven 依赖下载失败？ <a class="header-anchor" href="#q-maven-依赖下载失败" aria-label="Permalink to &quot;Q: Maven 依赖下载失败？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 配置国内镜像源：</p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- settings.xml 添加阿里云镜像 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirror</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>aliyunmaven&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirrorOf</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>*&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirrorOf</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>阿里云公共仓库&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>https://maven.aliyun.com/repository/public&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirror</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="q-node-依赖安装缓慢" tabindex="-1">Q: Node 依赖安装缓慢？ <a class="header-anchor" href="#q-node-依赖安装缓慢" aria-label="Permalink to &quot;Q: Node 依赖安装缓慢？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 配置淘宝镜像：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 设置镜像源</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> config</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> set</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> registry</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://registry.npmmirror.com</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或使用 pnpm</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> config</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> set</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> registry</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://registry.npmmirror.com</span></span></code></pre>
</div><h2 id="启动问题" tabindex="-1">启动问题 <a class="header-anchor" href="#启动问题" aria-label="Permalink to &quot;启动问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-nacos-连接失败" tabindex="-1">Q: Nacos 连接失败？ <a class="header-anchor" href="#q-nacos-连接失败" aria-label="Permalink to &quot;Q: Nacos 连接失败？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 检查以下几点：</p>
<ol>
<li><strong>确认 Nacos 已启动</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> grep</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><ol start="2">
<li><strong>确认端口暴露</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Nacos 2.x 需要暴露 8848 和 9848 两个端口</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span></code></pre>
</div><ol start="3">
<li><strong>检查命名空间配置</strong>
<ul>
<li>确保 Nacos 中创建了 <code>v4-dev</code> 命名空间</li>
<li>或注释掉 <code>application.yml</code> 中的 <code>namespace</code> 配置</li>
</ul>
</li>
</ol>
<h3 id="q-后端启动报-无法找到数据源" tabindex="-1">Q: 后端启动报 &quot;无法找到数据源&quot;？ <a class="header-anchor" href="#q-后端启动报-无法找到数据源" aria-label="Permalink to &quot;Q: 后端启动报 &quot;无法找到数据源&quot;？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 按以下步骤检查：</p>
<ol>
<li>确认 MySQL 已启动并可连接</li>
<li>确认数据库 <code>v4-dev</code> 已创建</li>
<li>确认 Nacos 配置已导入且配置正确</li>
<li>检查 Nacos 中数据库连接信息</li>
</ol>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查 Nacos 配置中的数据库连接</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/v4-dev?...</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.datasource.master.username=root</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.datasource.master.password=123456</span></span></code></pre>
</div><h3 id="q-前端启动后页面空白" tabindex="-1">Q: 前端启动后页面空白？ <a class="header-anchor" href="#q-前端启动后页面空白" aria-label="Permalink to &quot;Q: 前端启动后页面空白？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 检查以下几点：</p>
<ol>
<li>
<p><strong>确认后端服务已启动</strong></p>
<ul>
<li>Gateway (9000)</li>
<li>IAM (5001)</li>
<li>Suite (5002)</li>
</ul>
</li>
<li>
<p><strong>检查代理配置</strong></p>
</li>
</ol>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// vite.config.ts 中检查代理目标</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">proxy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  '/api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    target: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'http://localhost:9000'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 确保指向本地网关</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    changeOrigin: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><ol start="3">
<li><strong>清除缓存重试</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> node_modules/.vite</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><h2 id="登录问题" tabindex="-1">登录问题 <a class="header-anchor" href="#登录问题" aria-label="Permalink to &quot;登录问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-登录提示租户不存在" tabindex="-1">Q: 登录提示租户不存在？ <a class="header-anchor" href="#q-登录提示租户不存在" aria-label="Permalink to &quot;Q: 登录提示租户不存在？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 确认输入了正确的租户编码：</p>
<table tabindex="0">
<thead>
<tr>
<th>租户类型</th>
<th>租户编码</th>
<th>账号</th>
<th>密码</th>
</tr>
</thead>
<tbody>
<tr>
<td>平台管理员</td>
<td>0000</td>
<td>admin</td>
<td>123456</td>
</tr>
<tr>
<td>租户管理员</td>
<td>8888</td>
<td>admin</td>
<td>123456</td>
</tr>
</tbody>
</table>
<h3 id="q-登录提示验证码错误" tabindex="-1">Q: 登录提示验证码错误？ <a class="header-anchor" href="#q-登录提示验证码错误" aria-label="Permalink to &quot;Q: 登录提示验证码错误？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 可能原因：</p>
<ol>
<li><strong>Redis 未启动</strong> - 验证码存储在 Redis 中</li>
<li><strong>Redis 连接配置错误</strong> - 检查 Nacos 中 Redis 配置</li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查 Redis 是否正常</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -it</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis-cli</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ping</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 应返回 PONG</span></span></code></pre>
</div><h3 id="q-登录后权限不足" tabindex="-1">Q: 登录后权限不足？ <a class="header-anchor" href="#q-登录后权限不足" aria-label="Permalink to &quot;Q: 登录后权限不足？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 检查用户角色配置：</p>
<ol>
<li>登录平台管理员账号（租户 0000）</li>
<li>进入「系统管理」-「角色管理」</li>
<li>确认角色已分配相应菜单权限</li>
</ol>
<h2 id="调试排错" tabindex="-1">调试排错 <a class="header-anchor" href="#调试排错" aria-label="Permalink to &quot;调试排错&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-控制台一堆红色报错-看不懂怎么办" tabindex="-1">Q: 控制台一堆红色报错，看不懂怎么办？ <a class="header-anchor" href="#q-控制台一堆红色报错-看不懂怎么办" aria-label="Permalink to &quot;Q: 控制台一堆红色报错，看不懂怎么办？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 看报错的关键技巧：</p>
<ol>
<li><strong>找 <code>Caused by</code></strong> - 真正的错误原因通常在这行</li>
<li><strong>找你写的代码</strong> - 堆栈里找 <code>com.wemirr</code> 开头的行</li>
<li><strong>看第一个错</strong> - 后面的错误可能是连锁反应</li>
</ol>
<p>常见错误关键词：</p>
<ul>
<li><code>Connection refused</code> → 连不上数据库/Redis/Nacos</li>
<li><code>NullPointerException</code> → 空指针，某个对象是 null</li>
<li><code>Access denied</code> → 数据库密码错了</li>
<li><code>Table doesn't exist</code> → 表不存在，SQL 脚本没执行</li>
</ul>
<h3 id="q-怎么知道后端服务有没有启动成功" tabindex="-1">Q: 怎么知道后端服务有没有启动成功？ <a class="header-anchor" href="#q-怎么知道后端服务有没有启动成功" aria-label="Permalink to &quot;Q: 怎么知道后端服务有没有启动成功？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 看控制台日志最后几行：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Started GatewayApplication in 5.xxx seconds</span></span></code></pre>
</div><p>如果看到 <code>Started xxx</code> 就是启动成功了。</p>
<p>也可以访问 Nacos 控制台 → 服务列表，看服务有没有注册上去。</p>
<h3 id="q-接口报错怎么调试" tabindex="-1">Q: 接口报错怎么调试？ <a class="header-anchor" href="#q-接口报错怎么调试" aria-label="Permalink to &quot;Q: 接口报错怎么调试？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ol>
<li><strong>浏览器 F12</strong> → Network 标签 → 找到报错的请求</li>
<li>看 Response 里的错误信息</li>
<li>后端 IDEA 控制台看详细报错</li>
</ol>
<h3 id="q-前端页面改了代码没效果" tabindex="-1">Q: 前端页面改了代码没效果？ <a class="header-anchor" href="#q-前端页面改了代码没效果" aria-label="Permalink to &quot;Q: 前端页面改了代码没效果？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ol>
<li>先看有没有保存文件（Ctrl+S）</li>
<li>清浏览器缓存（Ctrl+Shift+Delete）</li>
<li>重启前端服务（Ctrl+C 停止，再 <code>pnpm dev:antd</code>）</li>
</ol>
<hr>
<h2 id="开发问题" tabindex="-1">开发问题 <a class="header-anchor" href="#开发问题" aria-label="Permalink to &quot;开发问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-如何新增一个菜单" tabindex="-1">Q: 如何新增一个菜单？ <a class="header-anchor" href="#q-如何新增一个菜单" aria-label="Permalink to &quot;Q: 如何新增一个菜单？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 步骤如下：</p>
<ol>
<li><strong>后端</strong> - 编写 Controller、Service、Mapper</li>
<li><strong>前端</strong> - 在 <code>views/wemirr/</code> 下创建页面组件</li>
<li><strong>配置</strong> - 在「菜单管理」中添加菜单项，配置路由地址和组件路径</li>
</ol>
<h3 id="q-如何获取当前登录用户信息" tabindex="-1">Q: 如何获取当前登录用户信息？ <a class="header-anchor" href="#q-如何获取当前登录用户信息" aria-label="Permalink to &quot;Q: 如何获取当前登录用户信息？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 后端获取</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> example</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 前端获取</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useUserStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/store/modules/user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getUserInfo;</span></span></code></pre>
</div><h3 id="q-如何忽略某个接口的租户过滤" tabindex="-1">Q: 如何忽略某个接口的租户过滤？ <a class="header-anchor" href="#q-如何忽略某个接口的租户过滤" aria-label="Permalink to &quot;Q: 如何忽略某个接口的租户过滤？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 使用 <code>@TenantIgnore</code> 注解：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantIgnore</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/public/data"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getPublicData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 此接口不会添加租户过滤条件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="部署问题" tabindex="-1">部署问题 <a class="header-anchor" href="#部署问题" aria-label="Permalink to &quot;部署问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-生产环境如何配置" tabindex="-1">Q: 生产环境如何配置？ <a class="header-anchor" href="#q-生产环境如何配置" aria-label="Permalink to &quot;Q: 生产环境如何配置？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 参考 <a href="/zh/guide/docker/production.html">生产部署</a> 文档</p>
<h3 id="q-如何配置-https" tabindex="-1">Q: 如何配置 HTTPS？ <a class="header-anchor" href="#q-如何配置-https" aria-label="Permalink to &quot;Q: 如何配置 HTTPS？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 在 Nginx 中配置 SSL：</p>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your-domain.com;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/path/to/cert.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/path/to/key.pem;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:5666;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:9000;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="获取帮助" tabindex="-1">获取帮助 <a class="header-anchor" href="#获取帮助" aria-label="Permalink to &quot;获取帮助&quot;">&ZeroWidthSpace;</a></h2>
<p>如果以上内容没有解决您的问题：</p>
<ul>
<li>📖 查看 <a href="/zh/faq/">完整 FAQ</a></li>
<li>💬 加入技术交流群：789517089</li>
<li>🐛 提交 <a href="https://gitee.com/battcn/wemirr-platform/issues" target="_blank" rel="noreferrer">Issue</a></li>
<li>💰 付费技术支持：微信 <code>battcn2022</code></li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="新手常见问题" tabindex="-1">新手常见问题 <a class="header-anchor" href="#新手常见问题" aria-label="Permalink to &quot;新手常见问题&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>本页面收集了新手入门时最常遇到的问题和解决方案，包括一些&quot;看起来很蠢但确实会遇到&quot;的问题。</p>
</div>
<hr>
<h2 id="概念扫盲" tabindex="-1">概念扫盲 <a class="header-anchor" href="#概念扫盲" aria-label="Permalink to &quot;概念扫盲&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-这个项目为什么有这么多模块-我该启动哪个" tabindex="-1">Q: 这个项目为什么有这么多模块？我该启动哪个？ <a class="header-anchor" href="#q-这个项目为什么有这么多模块-我该启动哪个" aria-label="Permalink to &quot;Q: 这个项目为什么有这么多模块？我该启动哪个？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 这是微服务架构，不同服务负责不同功能：</p>
<table tabindex="0">
<thead>
<tr>
<th>服务</th>
<th>作用</th>
<th>必须启动？</th>
</tr>
</thead>
<tbody>
<tr>
<td>Gateway</td>
<td>网关，所有请求的入口</td>
<td>✅ 必须</td>
</tr>
<tr>
<td>IAM</td>
<td>认证授权，负责登录</td>
<td>✅ 必须</td>
</tr>
<tr>
<td>Suite</td>
<td>核心业务</td>
<td>✅ 必须</td>
</tr>
<tr>
<td>Workflow</td>
<td>工作流（可选）</td>
<td>❌ 按需</td>
</tr>
<tr>
<td>TMS</td>
<td>运输管理（可选）</td>
<td>❌ 按需</td>
</tr>
<tr>
<td>WMS</td>
<td>仓储管理（可选）</td>
<td>❌ 按需</td>
</tr>
</tbody>
</table>
<p><strong>最小启动</strong>：Gateway + IAM + Suite</p>
<h3 id="q-什么是-nacos-为什么要装这个" tabindex="-1">Q: 什么是 Nacos？为什么要装这个？ <a class="header-anchor" href="#q-什么是-nacos-为什么要装这个" aria-label="Permalink to &quot;Q: 什么是 Nacos？为什么要装这个？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Nacos 是配置中心 + 服务注册中心：</p>
<ul>
<li><strong>配置中心</strong>：数据库密码、Redis 地址等配置统一放 Nacos，不用改代码</li>
<li><strong>服务注册</strong>：各个微服务启动后会注册到 Nacos，网关才知道怎么转发请求</li>
</ul>
<p>不装 Nacos = 项目跑不起来。</p>
<h3 id="q-什么是租户-多租户是什么意思" tabindex="-1">Q: 什么是租户？多租户是什么意思？ <a class="header-anchor" href="#q-什么是租户-多租户是什么意思" aria-label="Permalink to &quot;Q: 什么是租户？多租户是什么意思？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 简单理解：</p>
<ul>
<li><strong>租户</strong> = 公司/组织</li>
<li><strong>多租户</strong> = 一套系统给多个公司用，数据互相隔离</li>
</ul>
<p>比如 SaaS 软件，A公司和B公司都用这套系统，但 A 看不到 B 的数据。</p>
<p>登录时填的「租户编码」就是区分你属于哪个公司：</p>
<ul>
<li><code>0000</code> = 平台管理员（超级管理员，能管所有租户）</li>
<li><code>8888</code> = 示例租户（普通租户）</li>
</ul>
<h3 id="q-前后端分离是什么意思" tabindex="-1">Q: 前后端分离是什么意思？ <a class="header-anchor" href="#q-前后端分离是什么意思" aria-label="Permalink to &quot;Q: 前后端分离是什么意思？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ul>
<li><strong>后端</strong>（wemirr-platform）：提供 API 接口，返回 JSON 数据</li>
<li><strong>前端</strong>（wemirr-platform-ui）：负责页面展示，调用后端接口获取数据</li>
</ul>
<p>两个项目独立运行，通过 HTTP 请求通信。</p>
<h3 id="q-token-是什么-为什么要-token" tabindex="-1">Q: Token 是什么？为什么要 Token？ <a class="header-anchor" href="#q-token-是什么-为什么要-token" aria-label="Permalink to &quot;Q: Token 是什么？为什么要 Token？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Token 是登录凭证，相当于&quot;门禁卡&quot;：</p>
<ol>
<li>登录成功 → 后端发一个 Token 给你</li>
<li>之后每次请求 → 带上这个 Token</li>
<li>后端验证 Token → 知道你是谁</li>
</ol>
<p>Token 过期了就需要重新登录。</p>
<hr>
<h2 id="工具使用" tabindex="-1">工具使用 <a class="header-anchor" href="#工具使用" aria-label="Permalink to &quot;工具使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-什么是-maven-我需要单独安装吗" tabindex="-1">Q: 什么是 Maven？我需要单独安装吗？ <a class="header-anchor" href="#q-什么是-maven-我需要单独安装吗" aria-label="Permalink to &quot;Q: 什么是 Maven？我需要单独安装吗？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Maven 是 Java 的包管理工具（类似前端的 npm）：</p>
<ul>
<li><strong>作用</strong>：自动下载项目依赖的 jar 包</li>
<li><strong>安装</strong>：IDEA 自带 Maven，不需要单独安装</li>
<li><strong>配置镜像</strong>：国内下载慢，需配置阿里云镜像（见下文）</li>
</ul>
<h3 id="q-idea-怎么打开这个项目" tabindex="-1">Q: IDEA 怎么打开这个项目？ <a class="header-anchor" href="#q-idea-怎么打开这个项目" aria-label="Permalink to &quot;Q: IDEA 怎么打开这个项目？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ol>
<li>File → Open → 选择 <code>wemirr-platform</code> 文件夹</li>
<li>等待右下角进度条走完（Maven 在下载依赖）</li>
<li>如果报红，等一会儿，或者点击右侧 Maven 图标 → 刷新按钮</li>
</ol>
<h3 id="q-pnpm-是什么-和-npm-有什么区别" tabindex="-1">Q: pnpm 是什么？和 npm 有什么区别？ <a class="header-anchor" href="#q-pnpm-是什么-和-npm-有什么区别" aria-label="Permalink to &quot;Q: pnpm 是什么？和 npm 有什么区别？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 都是前端包管理工具：</p>
<ul>
<li><strong>npm</strong>：Node.js 自带的，但比较慢</li>
<li><strong>pnpm</strong>：更快、更省空间的替代品</li>
</ul>
<p>本项目用 pnpm，安装命令：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span></code></pre>
</div><h3 id="q-我不会用-git-命令怎么办" tabindex="-1">Q: 我不会用 Git 命令怎么办？ <a class="header-anchor" href="#q-我不会用-git-命令怎么办" aria-label="Permalink to &quot;Q: 我不会用 Git 命令怎么办？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 最简单的方式：直接在 Gitee 页面下载 ZIP 压缩包</p>
<p>或者用图形化工具：</p>
<ul>
<li><strong>IDEA</strong>：自带 Git 功能，VCS → Get from Version Control</li>
<li><strong>SourceTree</strong>：免费的 Git 图形化客户端</li>
</ul>
<h3 id="q-docker-是什么-必须用吗" tabindex="-1">Q: Docker 是什么？必须用吗？ <a class="header-anchor" href="#q-docker-是什么-必须用吗" aria-label="Permalink to &quot;Q: Docker 是什么？必须用吗？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> Docker 是容器技术，可以一键启动 MySQL、Redis、Nacos 等中间件。</p>
<p><strong>必须用吗？</strong> 不是必须，但强烈推荐：</p>
<ul>
<li>用 Docker：一条命令启动所有环境</li>
<li>不用 Docker：需要手动安装 MySQL、Redis、Nacos...（很麻烦）</li>
</ul>
<hr>
<h2 id="环境问题" tabindex="-1">环境问题 <a class="header-anchor" href="#环境问题" aria-label="Permalink to &quot;环境问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-docker-启动失败" tabindex="-1">Q: Docker 启动失败？ <a class="header-anchor" href="#q-docker-启动失败" aria-label="Permalink to &quot;Q: Docker 启动失败？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 常见原因及解决方案：</p>
<ol>
<li><strong>端口被占用</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:3306</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:6379</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:8848</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 杀掉占用进程或修改 docker-compose 端口映射</span></span></code></pre>
</div><ol start="2">
<li><strong>内存不足</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Docker Desktop 设置中增加内存限制（建议至少 4GB）</span></span></code></pre>
</div><ol start="3">
<li><strong>网络问题</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 重新创建 Docker 网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span></code></pre>
</div><h3 id="q-maven-依赖下载失败" tabindex="-1">Q: Maven 依赖下载失败？ <a class="header-anchor" href="#q-maven-依赖下载失败" aria-label="Permalink to &quot;Q: Maven 依赖下载失败？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 配置国内镜像源：</p>
<div class="language-xml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">xml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- settings.xml 添加阿里云镜像 --></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirror</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>aliyunmaven&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirrorOf</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>*&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirrorOf</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>阿里云公共仓库&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>https://maven.aliyun.com/repository/public&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">mirror</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h3 id="q-node-依赖安装缓慢" tabindex="-1">Q: Node 依赖安装缓慢？ <a class="header-anchor" href="#q-node-依赖安装缓慢" aria-label="Permalink to &quot;Q: Node 依赖安装缓慢？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 配置淘宝镜像：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 设置镜像源</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> config</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> set</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> registry</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://registry.npmmirror.com</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 或使用 pnpm</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> config</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> set</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> registry</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://registry.npmmirror.com</span></span></code></pre>
</div><h2 id="启动问题" tabindex="-1">启动问题 <a class="header-anchor" href="#启动问题" aria-label="Permalink to &quot;启动问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-nacos-连接失败" tabindex="-1">Q: Nacos 连接失败？ <a class="header-anchor" href="#q-nacos-连接失败" aria-label="Permalink to &quot;Q: Nacos 连接失败？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 检查以下几点：</p>
<ol>
<li><strong>确认 Nacos 已启动</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ps</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> grep</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span></span></code></pre>
</div><ol start="2">
<li><strong>确认端口暴露</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Nacos 2.x 需要暴露 8848 和 9848 两个端口</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server</span></span></code></pre>
</div><ol start="3">
<li><strong>检查命名空间配置</strong>
<ul>
<li>确保 Nacos 中创建了 <code>v4-dev</code> 命名空间</li>
<li>或注释掉 <code>application.yml</code> 中的 <code>namespace</code> 配置</li>
</ul>
</li>
</ol>
<h3 id="q-后端启动报-无法找到数据源" tabindex="-1">Q: 后端启动报 &quot;无法找到数据源&quot;？ <a class="header-anchor" href="#q-后端启动报-无法找到数据源" aria-label="Permalink to &quot;Q: 后端启动报 &quot;无法找到数据源&quot;？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 按以下步骤检查：</p>
<ol>
<li>确认 MySQL 已启动并可连接</li>
<li>确认数据库 <code>v4-dev</code> 已创建</li>
<li>确认 Nacos 配置已导入且配置正确</li>
<li>检查 Nacos 中数据库连接信息</li>
</ol>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查 Nacos 配置中的数据库连接</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.datasource.master.url=jdbc:mysql://localhost:3306/v4-dev?...</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.datasource.master.username=root</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">spring.datasource.dynamic.datasource.master.password=123456</span></span></code></pre>
</div><h3 id="q-前端启动后页面空白" tabindex="-1">Q: 前端启动后页面空白？ <a class="header-anchor" href="#q-前端启动后页面空白" aria-label="Permalink to &quot;Q: 前端启动后页面空白？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 检查以下几点：</p>
<ol>
<li>
<p><strong>确认后端服务已启动</strong></p>
<ul>
<li>Gateway (9000)</li>
<li>IAM (5001)</li>
<li>Suite (5002)</li>
</ul>
</li>
<li>
<p><strong>检查代理配置</strong></p>
</li>
</ol>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// vite.config.ts 中检查代理目标</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">proxy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  '/api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    target: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'http://localhost:9000'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 确保指向本地网关</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    changeOrigin: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><ol start="3">
<li><strong>清除缓存重试</strong></li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> node_modules/.vite</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><h2 id="登录问题" tabindex="-1">登录问题 <a class="header-anchor" href="#登录问题" aria-label="Permalink to &quot;登录问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-登录提示租户不存在" tabindex="-1">Q: 登录提示租户不存在？ <a class="header-anchor" href="#q-登录提示租户不存在" aria-label="Permalink to &quot;Q: 登录提示租户不存在？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 确认输入了正确的租户编码：</p>
<table tabindex="0">
<thead>
<tr>
<th>租户类型</th>
<th>租户编码</th>
<th>账号</th>
<th>密码</th>
</tr>
</thead>
<tbody>
<tr>
<td>平台管理员</td>
<td>0000</td>
<td>admin</td>
<td>123456</td>
</tr>
<tr>
<td>租户管理员</td>
<td>8888</td>
<td>admin</td>
<td>123456</td>
</tr>
</tbody>
</table>
<h3 id="q-登录提示验证码错误" tabindex="-1">Q: 登录提示验证码错误？ <a class="header-anchor" href="#q-登录提示验证码错误" aria-label="Permalink to &quot;Q: 登录提示验证码错误？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 可能原因：</p>
<ol>
<li><strong>Redis 未启动</strong> - 验证码存储在 Redis 中</li>
<li><strong>Redis 连接配置错误</strong> - 检查 Nacos 中 Redis 配置</li>
</ol>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 检查 Redis 是否正常</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> exec</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -it</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis-cli</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ping</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 应返回 PONG</span></span></code></pre>
</div><h3 id="q-登录后权限不足" tabindex="-1">Q: 登录后权限不足？ <a class="header-anchor" href="#q-登录后权限不足" aria-label="Permalink to &quot;Q: 登录后权限不足？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 检查用户角色配置：</p>
<ol>
<li>登录平台管理员账号（租户 0000）</li>
<li>进入「系统管理」-「角色管理」</li>
<li>确认角色已分配相应菜单权限</li>
</ol>
<h2 id="调试排错" tabindex="-1">调试排错 <a class="header-anchor" href="#调试排错" aria-label="Permalink to &quot;调试排错&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-控制台一堆红色报错-看不懂怎么办" tabindex="-1">Q: 控制台一堆红色报错，看不懂怎么办？ <a class="header-anchor" href="#q-控制台一堆红色报错-看不懂怎么办" aria-label="Permalink to &quot;Q: 控制台一堆红色报错，看不懂怎么办？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 看报错的关键技巧：</p>
<ol>
<li><strong>找 <code>Caused by</code></strong> - 真正的错误原因通常在这行</li>
<li><strong>找你写的代码</strong> - 堆栈里找 <code>com.wemirr</code> 开头的行</li>
<li><strong>看第一个错</strong> - 后面的错误可能是连锁反应</li>
</ol>
<p>常见错误关键词：</p>
<ul>
<li><code>Connection refused</code> → 连不上数据库/Redis/Nacos</li>
<li><code>NullPointerException</code> → 空指针，某个对象是 null</li>
<li><code>Access denied</code> → 数据库密码错了</li>
<li><code>Table doesn't exist</code> → 表不存在，SQL 脚本没执行</li>
</ul>
<h3 id="q-怎么知道后端服务有没有启动成功" tabindex="-1">Q: 怎么知道后端服务有没有启动成功？ <a class="header-anchor" href="#q-怎么知道后端服务有没有启动成功" aria-label="Permalink to &quot;Q: 怎么知道后端服务有没有启动成功？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 看控制台日志最后几行：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Started GatewayApplication in 5.xxx seconds</span></span></code></pre>
</div><p>如果看到 <code>Started xxx</code> 就是启动成功了。</p>
<p>也可以访问 Nacos 控制台 → 服务列表，看服务有没有注册上去。</p>
<h3 id="q-接口报错怎么调试" tabindex="-1">Q: 接口报错怎么调试？ <a class="header-anchor" href="#q-接口报错怎么调试" aria-label="Permalink to &quot;Q: 接口报错怎么调试？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ol>
<li><strong>浏览器 F12</strong> → Network 标签 → 找到报错的请求</li>
<li>看 Response 里的错误信息</li>
<li>后端 IDEA 控制台看详细报错</li>
</ol>
<h3 id="q-前端页面改了代码没效果" tabindex="-1">Q: 前端页面改了代码没效果？ <a class="header-anchor" href="#q-前端页面改了代码没效果" aria-label="Permalink to &quot;Q: 前端页面改了代码没效果？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<ol>
<li>先看有没有保存文件（Ctrl+S）</li>
<li>清浏览器缓存（Ctrl+Shift+Delete）</li>
<li>重启前端服务（Ctrl+C 停止，再 <code>pnpm dev:antd</code>）</li>
</ol>
<hr>
<h2 id="开发问题" tabindex="-1">开发问题 <a class="header-anchor" href="#开发问题" aria-label="Permalink to &quot;开发问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-如何新增一个菜单" tabindex="-1">Q: 如何新增一个菜单？ <a class="header-anchor" href="#q-如何新增一个菜单" aria-label="Permalink to &quot;Q: 如何新增一个菜单？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 步骤如下：</p>
<ol>
<li><strong>后端</strong> - 编写 Controller、Service、Mapper</li>
<li><strong>前端</strong> - 在 <code>views/wemirr/</code> 下创建页面组件</li>
<li><strong>配置</strong> - 在「菜单管理」中添加菜单项，配置路由地址和组件路径</li>
</ol>
<h3 id="q-如何获取当前登录用户信息" tabindex="-1">Q: 如何获取当前登录用户信息？ <a class="header-anchor" href="#q-如何获取当前登录用户信息" aria-label="Permalink to &quot;Q: 如何获取当前登录用户信息？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong></p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 后端获取</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Autowired</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> example</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String username </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 前端获取</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useUserStore } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@/store/modules/user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useUserStore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> userInfo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userStore.getUserInfo;</span></span></code></pre>
</div><h3 id="q-如何忽略某个接口的租户过滤" tabindex="-1">Q: 如何忽略某个接口的租户过滤？ <a class="header-anchor" href="#q-如何忽略某个接口的租户过滤" aria-label="Permalink to &quot;Q: 如何忽略某个接口的租户过滤？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 使用 <code>@TenantIgnore</code> 注解：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantIgnore</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/public/data"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getPublicData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 此接口不会添加租户过滤条件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="部署问题" tabindex="-1">部署问题 <a class="header-anchor" href="#部署问题" aria-label="Permalink to &quot;部署问题&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="q-生产环境如何配置" tabindex="-1">Q: 生产环境如何配置？ <a class="header-anchor" href="#q-生产环境如何配置" aria-label="Permalink to &quot;Q: 生产环境如何配置？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 参考 <a href="/zh/guide/docker/production.html">生产部署</a> 文档</p>
<h3 id="q-如何配置-https" tabindex="-1">Q: 如何配置 HTTPS？ <a class="header-anchor" href="#q-如何配置-https" aria-label="Permalink to &quot;Q: 如何配置 HTTPS？&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>A:</strong> 在 Nginx 中配置 SSL：</p>
<div class="language-nginx vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">nginx</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your-domain.com;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/path/to/cert.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/path/to/key.pem;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:5666;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> /api </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        proxy_pass </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">http://localhost:9000;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="获取帮助" tabindex="-1">获取帮助 <a class="header-anchor" href="#获取帮助" aria-label="Permalink to &quot;获取帮助&quot;">&ZeroWidthSpace;</a></h2>
<p>如果以上内容没有解决您的问题：</p>
<ul>
<li>📖 查看 <a href="/zh/faq/">完整 FAQ</a></li>
<li>💬 加入技术交流群：789517089</li>
<li>🐛 提交 <a href="https://gitee.com/battcn/wemirr-platform/issues" target="_blank" rel="noreferrer">Issue</a></li>
<li>💰 付费技术支持：微信 <code>battcn2022</code></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[5分钟快速上手 ]]></title>
            <link>https://docs.battcn.com/zh/guide/quickstart/</link>
            <guid>https://docs.battcn.com/zh/guide/quickstart/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="_5分钟快速上手" tabindex="-1">5分钟快速上手 <a class="header-anchor" href="#_5分钟快速上手" aria-label="Permalink to &quot;5分钟快速上手&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">适合人群</p>
<p>零基础小白、想快速体验项目功能的开发者</p>
</div>
<h2 id="前置条件" tabindex="-1">前置条件 <a class="header-anchor" href="#前置条件" aria-label="Permalink to &quot;前置条件&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">⚠️ 小白必读</p>
<p>如果你是第一次接触 Java 微服务项目，请先确保以下软件<strong>已正确安装并配置好环境变量</strong>！</p>
</div>
<table tabindex="0">
<thead>
<tr>
<th>软件</th>
<th>最低版本</th>
<th>推荐版本</th>
<th>下载地址</th>
<th>验证命令</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK</td>
<td>17</td>
<td><strong>21</strong></td>
<td><a href="https://www.oracle.com/java/technologies/downloads/" target="_blank" rel="noreferrer">Oracle JDK</a></td>
<td><code>java -version</code></td>
</tr>
<tr>
<td>Maven</td>
<td>3.8+</td>
<td>3.9+</td>
<td><a href="https://maven.apache.org/download.cgi" target="_blank" rel="noreferrer">Maven</a></td>
<td><code>mvn -v</code></td>
</tr>
<tr>
<td>Node.js</td>
<td>18</td>
<td><strong>20+</strong></td>
<td><a href="https://nodejs.org/" target="_blank" rel="noreferrer">Node.js</a></td>
<td><code>node -v</code></td>
</tr>
<tr>
<td>pnpm</td>
<td>8+</td>
<td>9+</td>
<td>安装后执行 <code>npm i -g pnpm</code></td>
<td><code>pnpm -v</code></td>
</tr>
<tr>
<td>Docker</td>
<td>20+</td>
<td>最新版</td>
<td><a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noreferrer">Docker Desktop</a></td>
<td><code>docker -v</code></td>
</tr>
<tr>
<td>Git</td>
<td>2.0+</td>
<td>最新版</td>
<td><a href="https://git-scm.com/" target="_blank" rel="noreferrer">Git</a></td>
<td><code>git --version</code></td>
</tr>
<tr>
<td>IDE</td>
<td>-</td>
<td><strong>IDEA Ultimate</strong></td>
<td><a href="https://www.jetbrains.com/idea/" target="_blank" rel="noreferrer">JetBrains</a></td>
<td>-</td>
</tr>
</tbody>
</table>
<details class="details custom-block"><summary>🔍 如何验证环境是否安装成功？</summary>
<p>打开终端（Windows 用 CMD 或 PowerShell，Mac 用 Terminal），依次输入：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -version</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 应显示 java version "21.x.x" 或 "17.x.x"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">           # 应显示 Apache Maven 3.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">node</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 应显示 v20.x.x 或 v18.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 应显示 9.x.x 或 8.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 应显示 Docker version 2x.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --version</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 应显示 git version 2.x.x</span></span></code></pre>
</div><p><strong>如果提示&quot;命令未找到&quot;</strong>，说明该软件未安装或环境变量未配置，请参考 <a href="/zh/guide/quickstart/faq.html#环境问题">环境安装指南</a></p>
</details>
<hr>
<h2 id="第一步-一键启动环境" tabindex="-1">第一步：一键启动环境 <a class="header-anchor" href="#第一步-一键启动环境" aria-label="Permalink to &quot;第一步：一键启动环境&quot;">&ZeroWidthSpace;</a></h2>
<div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-vKcUB" id="tab-ilgoaGK" checked><label data-title="Docker Compose (推荐)" for="tab-ilgoaGK">Docker Compose (推荐)</label><input type="radio" name="group-vKcUB" id="tab-uaZpqIt" ><label data-title="手动安装" for="tab-uaZpqIt">手动安装</label></div><div class="blocks">
<div class="language-bash vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建项目目录</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 下载 docker-compose 文件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -O</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform/raw/v4-dev/附件/docker/docker-compose.yml</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 一键启动所有中间件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span></code></pre>
</div><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建 Docker 网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 Redis</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 MySQL</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 Nacos</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server:latest</span></span></code></pre>
</div></div></div>
<h2 id="第二步-获取代码" tabindex="-1">第二步：获取代码 <a class="header-anchor" href="#第二步-获取代码" aria-label="Permalink to &quot;第二步：获取代码&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆后端代码（指定 v4-dev 分支）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆前端代码（指定 v4-dev 分支）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div><details class="details custom-block"><summary>🐢 克隆太慢？试试这个</summary>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 使用浅克隆，只拉取最近一次提交</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --depth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --depth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div></details>
<hr>
<h2 id="第三步-初始化数据库" tabindex="-1">第三步：初始化数据库 <a class="header-anchor" href="#第三步-初始化数据库" aria-label="Permalink to &quot;第三步：初始化数据库&quot;">&ZeroWidthSpace;</a></h2>
<div class="info custom-block"><p class="custom-block-title">📦 需要什么工具？</p>
<p>推荐使用 <strong>Navicat</strong>、<strong>DBeaver</strong>（免费）、<strong>DataGrip</strong> 或 IDEA 自带的 Database 工具</p>
</div>
<p><strong>步骤：</strong></p>
<ol>
<li><strong>连接 MySQL</strong> — 地址 <code>localhost:3306</code>，用户 <code>root</code>，密码 <code>123456</code></li>
<li><strong>创建数据库</strong> — 执行以下 SQL：</li>
</ol>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DATABASE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IF</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> EXISTS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> v4</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">dev </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> CHARACTER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> SET</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> utf8mb4 </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> COLLATE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> utf8mb4_general_ci;</span></span></code></pre>
</div><ol start="3">
<li><strong>导入数据</strong> — 选中 <code>v4-dev</code> 库，执行 SQL 脚本文件：</li>
</ol>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform/附件/mysql/v4-dev.sql</span></span></code></pre>
</div><div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<ul>
<li><strong>连接失败</strong> — 检查 Docker 容器是否启动：<code>docker ps | grep mysql</code></li>
<li><strong>导入报错</strong> — 确保选中的是 <code>v4-dev</code> 数据库再执行 SQL</li>
<li><strong>乱码</strong> — 确保创建库时用 <code>utf8mb4</code> 字符集</li>
</ul>
</div>
<hr>
<h2 id="第四步-导入-nacos-配置" tabindex="-1">第四步：导入 Nacos 配置 <a class="header-anchor" href="#第四步-导入-nacos-配置" aria-label="Permalink to &quot;第四步：导入 Nacos 配置&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>步骤：</strong></p>
<ol>
<li><strong>打开 Nacos</strong> — 浏览器访问 <a href="http://localhost:8848/nacos" target="_blank" rel="noreferrer">http://localhost:8848/nacos</a></li>
<li><strong>登录</strong> — 账号 <code>nacos</code>，密码 <code>nacos</code></li>
<li><strong>创建命名空间</strong>：
<ul>
<li>点击左侧菜单「命名空间」→「新建命名空间」</li>
<li>命名空间ID：<code>v4-dev</code>（<strong>必须完全一致</strong>）</li>
<li>命名空间名：<code>v4-dev</code></li>
</ul>
</li>
<li><strong>导入配置</strong>：
<ul>
<li>点击「配置管理」→「配置列表」</li>
<li>右上角切换命名空间到 <code>v4-dev</code></li>
<li>点击「导入配置」→ 选择文件 <code>wemirr-platform/附件/nacos/nacos_config_export.zip</code></li>
</ul>
</li>
</ol>
<p><img src="/env/nacos-namespace.png" alt="Nacos命名空间"></p>
<div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<ul>
<li><strong>命名空间 ID 不对</strong> — 必须是 <code>v4-dev</code>，不是随机生成的 UUID</li>
<li><strong>导入后配置为空</strong> — 检查是否切换到了 <code>v4-dev</code> 命名空间</li>
<li><strong>配置中的 IP 不对</strong> — 如果用 Docker，MySQL/Redis 地址应该是 <code>host.docker.internal</code> 或容器名</li>
</ul>
</div>
<h2 id="第五步-启动后端服务" tabindex="-1">第五步：启动后端服务 <a class="header-anchor" href="#第五步-启动后端服务" aria-label="Permalink to &quot;第五步：启动后端服务&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>步骤：</strong></p>
<ol>
<li><strong>用 IDEA 打开项目</strong> — 菜单 <code>File</code> → <code>Open</code> → 选择 <code>wemirr-platform</code> 文件夹</li>
<li><strong>等待索引完成</strong> — 右下角进度条跑完（首次约 3-5 分钟）</li>
<li><strong>依次启动服务</strong>：</li>
</ol>
<table tabindex="0">
<thead>
<tr>
<th style="text-align:center">顺序</th>
<th>服务</th>
<th>启动类位置</th>
<th style="text-align:center">端口</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">①</td>
<td>网关服务</td>
<td><code>wemirr-platform-gateway</code> → <code>GatewayApplication</code></td>
<td style="text-align:center">9000</td>
</tr>
<tr>
<td style="text-align:center">②</td>
<td>认证服务</td>
<td><code>wemirr-platform-iam</code> → <code>IamApplication</code></td>
<td style="text-align:center">5001</td>
</tr>
<tr>
<td style="text-align:center">③</td>
<td>业务服务</td>
<td><code>wemirr-platform-suite</code> → <code>SuiteApplication</code></td>
<td style="text-align:center">5002</td>
</tr>
</tbody>
</table>
<div class="tip custom-block"><p class="custom-block-title">💡 如何启动？</p>
<p>在 IDEA 中找到对应模块的 <code>Application.java</code> 文件，右键 → <code>Run 'xxxApplication'</code></p>
</div>
<div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<p><strong>Maven 依赖报错（红色波浪线）</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clean</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -DskipTests</span></span></code></pre>
</div><p><strong>端口被占用</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Mac/Linux 查看端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:5001</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Windows 查看端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">netstat</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -ano</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> findstr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 5001</span></span></code></pre>
</div><p><strong>连接 Nacos 失败</strong></p>
<ul>
<li>检查 Nacos 是否启动：<code>docker ps | grep nacos</code></li>
<li>检查 <code>bootstrap.yml</code> 中的 Nacos 地址是否正确</li>
</ul>
<p><strong>连接 MySQL/Redis 失败</strong></p>
<ul>
<li>检查 Docker 容器是否运行：<code>docker ps</code></li>
<li>检查 Nacos 配置中的连接地址和密码是否正确</li>
</ul>
</div>
<h2 id="第六步-启动前端项目" tabindex="-1">第六步：启动前端项目 <a class="header-anchor" href="#第六步-启动前端项目" aria-label="Permalink to &quot;第六步：启动前端项目&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>步骤：</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 1. 进入前端目录</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-ui</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 2. 安装 pnpm（如果没有）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 3. 安装依赖（首次需要，约 2-3 分钟）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 4. 启动开发服务器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><p>启动成功后会显示：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>  VITE v5.x.x  ready in xxx ms</span></span>
<span class="line"><span></span></span>
<span class="line"><span>  ➜  Local:   http://localhost:5666/</span></span>
<span class="line"><span>  ➜  Network: http://192.168.x.x:5666/</span></span></code></pre>
</div><div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<p><strong><code>pnpm: command not found</code></strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span></code></pre>
</div><p><strong>安装依赖报错 / 卡住</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 清除缓存重试</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> store</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> prune</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> node_modules</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span></code></pre>
</div><p><strong>启动后页面空白 / 报错</strong></p>
<ul>
<li>检查后端服务是否全部启动</li>
<li>检查浏览器控制台（F12）报错信息</li>
<li>确认 <code>.env.development</code> 中的 API 地址配置正确</li>
</ul>
</div>
<hr>
<h2 id="第七步-访问系统" tabindex="-1">第七步：访问系统 <a class="header-anchor" href="#第七步-访问系统" aria-label="Permalink to &quot;第七步：访问系统&quot;">&ZeroWidthSpace;</a></h2>
<p>🎉 恭喜！项目已成功运行，现在可以访问：</p>
<table tabindex="0">
<thead>
<tr>
<th>地址</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://localhost:5666" target="_blank" rel="noreferrer">http://localhost:5666</a></td>
<td>前端界面</td>
</tr>
<tr>
<td><a href="http://localhost:9000" target="_blank" rel="noreferrer">http://localhost:9000</a></td>
<td>后端 API</td>
</tr>
<tr>
<td><a href="http://localhost:8848/nacos" target="_blank" rel="noreferrer">http://localhost:8848/nacos</a></td>
<td>Nacos 控制台</td>
</tr>
</tbody>
</table>
<p><strong>默认登录账号：</strong></p>
<ul>
<li>平台管理员：租户编码 <code>0000</code>，账号 <code>admin</code>，密码 <code>123456</code></li>
<li>租户管理员：租户编码 <code>8888</code>，账号 <code>admin</code>，密码 <code>123456</code></li>
</ul>
<h2 id="快速体验" tabindex="-1">快速体验 <a class="header-anchor" href="#快速体验" aria-label="Permalink to &quot;快速体验&quot;">&ZeroWidthSpace;</a></h2>
<p>登录后您可以体验以下核心功能：</p>
<ul>
<li><strong>🏢 租户管理</strong> - 创建和管理多租户</li>
<li><strong>👥 用户管理</strong> - 用户、角色、权限配置</li>
<li><strong>📋 菜单管理</strong> - 动态菜单配置</li>
<li><strong>🔐 权限控制</strong> - RBAC 权限模型</li>
<li><strong>📊 系统监控</strong> - 日志、操作记录</li>
</ul>
<h2 id="遇到问题" tabindex="-1">遇到问题？ <a class="header-anchor" href="#遇到问题" aria-label="Permalink to &quot;遇到问题？&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li>📖 查看 <a href="/zh/faq/">常见问题</a></li>
<li>💬 加入技术交流群：789517089</li>
<li>🐛 提交 <a href="https://gitee.com/battcn/wemirr-platform/issues" target="_blank" rel="noreferrer">Issue</a></li>
</ul>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/quickstart/concept.html">核心概念</a> - 了解 SaaS、多租户等基础概念</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 深入学习 Docker 部署方案</li>
<li><a href="/zh/guide/frontend/">前端开发</a> - 学习前端组件和页面开发</li>
<li><a href="/zh/guide/backend/">后端开发</a> - 学习后端 API 和服务开发</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="_5分钟快速上手" tabindex="-1">5分钟快速上手 <a class="header-anchor" href="#_5分钟快速上手" aria-label="Permalink to &quot;5分钟快速上手&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">适合人群</p>
<p>零基础小白、想快速体验项目功能的开发者</p>
</div>
<h2 id="前置条件" tabindex="-1">前置条件 <a class="header-anchor" href="#前置条件" aria-label="Permalink to &quot;前置条件&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">⚠️ 小白必读</p>
<p>如果你是第一次接触 Java 微服务项目，请先确保以下软件<strong>已正确安装并配置好环境变量</strong>！</p>
</div>
<table tabindex="0">
<thead>
<tr>
<th>软件</th>
<th>最低版本</th>
<th>推荐版本</th>
<th>下载地址</th>
<th>验证命令</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK</td>
<td>17</td>
<td><strong>21</strong></td>
<td><a href="https://www.oracle.com/java/technologies/downloads/" target="_blank" rel="noreferrer">Oracle JDK</a></td>
<td><code>java -version</code></td>
</tr>
<tr>
<td>Maven</td>
<td>3.8+</td>
<td>3.9+</td>
<td><a href="https://maven.apache.org/download.cgi" target="_blank" rel="noreferrer">Maven</a></td>
<td><code>mvn -v</code></td>
</tr>
<tr>
<td>Node.js</td>
<td>18</td>
<td><strong>20+</strong></td>
<td><a href="https://nodejs.org/" target="_blank" rel="noreferrer">Node.js</a></td>
<td><code>node -v</code></td>
</tr>
<tr>
<td>pnpm</td>
<td>8+</td>
<td>9+</td>
<td>安装后执行 <code>npm i -g pnpm</code></td>
<td><code>pnpm -v</code></td>
</tr>
<tr>
<td>Docker</td>
<td>20+</td>
<td>最新版</td>
<td><a href="https://www.docker.com/products/docker-desktop/" target="_blank" rel="noreferrer">Docker Desktop</a></td>
<td><code>docker -v</code></td>
</tr>
<tr>
<td>Git</td>
<td>2.0+</td>
<td>最新版</td>
<td><a href="https://git-scm.com/" target="_blank" rel="noreferrer">Git</a></td>
<td><code>git --version</code></td>
</tr>
<tr>
<td>IDE</td>
<td>-</td>
<td><strong>IDEA Ultimate</strong></td>
<td><a href="https://www.jetbrains.com/idea/" target="_blank" rel="noreferrer">JetBrains</a></td>
<td>-</td>
</tr>
</tbody>
</table>
<details class="details custom-block"><summary>🔍 如何验证环境是否安装成功？</summary>
<p>打开终端（Windows 用 CMD 或 PowerShell，Mac 用 Terminal），依次输入：</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">java</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -version</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 应显示 java version "21.x.x" 或 "17.x.x"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">           # 应显示 Apache Maven 3.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">node</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 应显示 v20.x.x 或 v18.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">          # 应显示 9.x.x 或 8.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -v</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 应显示 Docker version 2x.x.x</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --version</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    # 应显示 git version 2.x.x</span></span></code></pre>
</div><p><strong>如果提示&quot;命令未找到&quot;</strong>，说明该软件未安装或环境变量未配置，请参考 <a href="/zh/guide/quickstart/faq.html#环境问题">环境安装指南</a></p>
</details>
<hr>
<h2 id="第一步-一键启动环境" tabindex="-1">第一步：一键启动环境 <a class="header-anchor" href="#第一步-一键启动环境" aria-label="Permalink to &quot;第一步：一键启动环境&quot;">&ZeroWidthSpace;</a></h2>
<div class="vp-code-group vp-adaptive-theme"><div class="tabs"><input type="radio" name="group-vKcUB" id="tab-ilgoaGK" checked><label data-title="Docker Compose (推荐)" for="tab-ilgoaGK">Docker Compose (推荐)</label><input type="radio" name="group-vKcUB" id="tab-uaZpqIt" ><label data-title="手动安装" for="tab-uaZpqIt">手动安装</label></div><div class="blocks">
<div class="language-bash vp-adaptive-theme active"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建项目目录</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 下载 docker-compose 文件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">curl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -O</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform/raw/v4-dev/附件/docker/docker-compose.yml</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 一键启动所有中间件</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker-compose</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> up</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span></span></code></pre>
</div><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 创建 Docker 网络</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> network</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 Redis</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 6379:6379</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> redis:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 MySQL</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 3306:3306</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MYSQL_ROOT_PASSWORD=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> mysql:latest</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 启动 Nacos</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">docker</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -d</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --name</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --net</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 8848:8848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 9848:9848</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> \</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  -e</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> MODE=standalone</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> nacos/nacos-server:latest</span></span></code></pre>
</div></div></div>
<h2 id="第二步-获取代码" tabindex="-1">第二步：获取代码 <a class="header-anchor" href="#第二步-获取代码" aria-label="Permalink to &quot;第二步：获取代码&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆后端代码（指定 v4-dev 分支）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 克隆前端代码（指定 v4-dev 分支）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div><details class="details custom-block"><summary>🐢 克隆太慢？试试这个</summary>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 使用浅克隆，只拉取最近一次提交</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --depth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform.git</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clone</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -b</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> v4-dev</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --depth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://gitee.com/battcn/wemirr-platform-ui.git</span></span></code></pre>
</div></details>
<hr>
<h2 id="第三步-初始化数据库" tabindex="-1">第三步：初始化数据库 <a class="header-anchor" href="#第三步-初始化数据库" aria-label="Permalink to &quot;第三步：初始化数据库&quot;">&ZeroWidthSpace;</a></h2>
<div class="info custom-block"><p class="custom-block-title">📦 需要什么工具？</p>
<p>推荐使用 <strong>Navicat</strong>、<strong>DBeaver</strong>（免费）、<strong>DataGrip</strong> 或 IDEA 自带的 Database 工具</p>
</div>
<p><strong>步骤：</strong></p>
<ol>
<li><strong>连接 MySQL</strong> — 地址 <code>localhost:3306</code>，用户 <code>root</code>，密码 <code>123456</code></li>
<li><strong>创建数据库</strong> — 执行以下 SQL：</li>
</ol>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DATABASE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IF</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> EXISTS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> v4</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">dev </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> CHARACTER</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> SET</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> utf8mb4 </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DEFAULT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> COLLATE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> utf8mb4_general_ci;</span></span></code></pre>
</div><ol start="3">
<li><strong>导入数据</strong> — 选中 <code>v4-dev</code> 库，执行 SQL 脚本文件：</li>
</ol>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>wemirr-platform/附件/mysql/v4-dev.sql</span></span></code></pre>
</div><div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<ul>
<li><strong>连接失败</strong> — 检查 Docker 容器是否启动：<code>docker ps | grep mysql</code></li>
<li><strong>导入报错</strong> — 确保选中的是 <code>v4-dev</code> 数据库再执行 SQL</li>
<li><strong>乱码</strong> — 确保创建库时用 <code>utf8mb4</code> 字符集</li>
</ul>
</div>
<hr>
<h2 id="第四步-导入-nacos-配置" tabindex="-1">第四步：导入 Nacos 配置 <a class="header-anchor" href="#第四步-导入-nacos-配置" aria-label="Permalink to &quot;第四步：导入 Nacos 配置&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>步骤：</strong></p>
<ol>
<li><strong>打开 Nacos</strong> — 浏览器访问 <a href="http://localhost:8848/nacos" target="_blank" rel="noreferrer">http://localhost:8848/nacos</a></li>
<li><strong>登录</strong> — 账号 <code>nacos</code>，密码 <code>nacos</code></li>
<li><strong>创建命名空间</strong>：
<ul>
<li>点击左侧菜单「命名空间」→「新建命名空间」</li>
<li>命名空间ID：<code>v4-dev</code>（<strong>必须完全一致</strong>）</li>
<li>命名空间名：<code>v4-dev</code></li>
</ul>
</li>
<li><strong>导入配置</strong>：
<ul>
<li>点击「配置管理」→「配置列表」</li>
<li>右上角切换命名空间到 <code>v4-dev</code></li>
<li>点击「导入配置」→ 选择文件 <code>wemirr-platform/附件/nacos/nacos_config_export.zip</code></li>
</ul>
</li>
</ol>
<p><img src="/env/nacos-namespace.png" alt="Nacos命名空间"></p>
<div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<ul>
<li><strong>命名空间 ID 不对</strong> — 必须是 <code>v4-dev</code>，不是随机生成的 UUID</li>
<li><strong>导入后配置为空</strong> — 检查是否切换到了 <code>v4-dev</code> 命名空间</li>
<li><strong>配置中的 IP 不对</strong> — 如果用 Docker，MySQL/Redis 地址应该是 <code>host.docker.internal</code> 或容器名</li>
</ul>
</div>
<h2 id="第五步-启动后端服务" tabindex="-1">第五步：启动后端服务 <a class="header-anchor" href="#第五步-启动后端服务" aria-label="Permalink to &quot;第五步：启动后端服务&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>步骤：</strong></p>
<ol>
<li><strong>用 IDEA 打开项目</strong> — 菜单 <code>File</code> → <code>Open</code> → 选择 <code>wemirr-platform</code> 文件夹</li>
<li><strong>等待索引完成</strong> — 右下角进度条跑完（首次约 3-5 分钟）</li>
<li><strong>依次启动服务</strong>：</li>
</ol>
<table tabindex="0">
<thead>
<tr>
<th style="text-align:center">顺序</th>
<th>服务</th>
<th>启动类位置</th>
<th style="text-align:center">端口</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">①</td>
<td>网关服务</td>
<td><code>wemirr-platform-gateway</code> → <code>GatewayApplication</code></td>
<td style="text-align:center">9000</td>
</tr>
<tr>
<td style="text-align:center">②</td>
<td>认证服务</td>
<td><code>wemirr-platform-iam</code> → <code>IamApplication</code></td>
<td style="text-align:center">5001</td>
</tr>
<tr>
<td style="text-align:center">③</td>
<td>业务服务</td>
<td><code>wemirr-platform-suite</code> → <code>SuiteApplication</code></td>
<td style="text-align:center">5002</td>
</tr>
</tbody>
</table>
<div class="tip custom-block"><p class="custom-block-title">💡 如何启动？</p>
<p>在 IDEA 中找到对应模块的 <code>Application.java</code> 文件，右键 → <code>Run 'xxxApplication'</code></p>
</div>
<div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<p><strong>Maven 依赖报错（红色波浪线）</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mvn</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> clean</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -DskipTests</span></span></code></pre>
</div><p><strong>端口被占用</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Mac/Linux 查看端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsof</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -i:5001</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># Windows 查看端口占用</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">netstat</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -ano</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> findstr</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 5001</span></span></code></pre>
</div><p><strong>连接 Nacos 失败</strong></p>
<ul>
<li>检查 Nacos 是否启动：<code>docker ps | grep nacos</code></li>
<li>检查 <code>bootstrap.yml</code> 中的 Nacos 地址是否正确</li>
</ul>
<p><strong>连接 MySQL/Redis 失败</strong></p>
<ul>
<li>检查 Docker 容器是否运行：<code>docker ps</code></li>
<li>检查 Nacos 配置中的连接地址和密码是否正确</li>
</ul>
</div>
<h2 id="第六步-启动前端项目" tabindex="-1">第六步：启动前端项目 <a class="header-anchor" href="#第六步-启动前端项目" aria-label="Permalink to &quot;第六步：启动前端项目&quot;">&ZeroWidthSpace;</a></h2>
<p><strong>步骤：</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 1. 进入前端目录</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wemirr-platform-ui</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 2. 安装 pnpm（如果没有）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 3. 安装依赖（首次需要，约 2-3 分钟）</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 4. 启动开发服务器</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev:antd</span></span></code></pre>
</div><p>启动成功后会显示：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>  VITE v5.x.x  ready in xxx ms</span></span>
<span class="line"><span></span></span>
<span class="line"><span>  ➜  Local:   http://localhost:5666/</span></span>
<span class="line"><span>  ➜  Network: http://192.168.x.x:5666/</span></span></code></pre>
</div><div class="danger custom-block"><p class="custom-block-title">❌ 常见错误</p>
<p><strong><code>pnpm: command not found</code></strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -g</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pnpm</span></span></code></pre>
</div><p><strong>安装依赖报错 / 卡住</strong></p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 清除缓存重试</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> store</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> prune</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">rm</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -rf</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> node_modules</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pnpm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span></span></code></pre>
</div><p><strong>启动后页面空白 / 报错</strong></p>
<ul>
<li>检查后端服务是否全部启动</li>
<li>检查浏览器控制台（F12）报错信息</li>
<li>确认 <code>.env.development</code> 中的 API 地址配置正确</li>
</ul>
</div>
<hr>
<h2 id="第七步-访问系统" tabindex="-1">第七步：访问系统 <a class="header-anchor" href="#第七步-访问系统" aria-label="Permalink to &quot;第七步：访问系统&quot;">&ZeroWidthSpace;</a></h2>
<p>🎉 恭喜！项目已成功运行，现在可以访问：</p>
<table tabindex="0">
<thead>
<tr>
<th>地址</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://localhost:5666" target="_blank" rel="noreferrer">http://localhost:5666</a></td>
<td>前端界面</td>
</tr>
<tr>
<td><a href="http://localhost:9000" target="_blank" rel="noreferrer">http://localhost:9000</a></td>
<td>后端 API</td>
</tr>
<tr>
<td><a href="http://localhost:8848/nacos" target="_blank" rel="noreferrer">http://localhost:8848/nacos</a></td>
<td>Nacos 控制台</td>
</tr>
</tbody>
</table>
<p><strong>默认登录账号：</strong></p>
<ul>
<li>平台管理员：租户编码 <code>0000</code>，账号 <code>admin</code>，密码 <code>123456</code></li>
<li>租户管理员：租户编码 <code>8888</code>，账号 <code>admin</code>，密码 <code>123456</code></li>
</ul>
<h2 id="快速体验" tabindex="-1">快速体验 <a class="header-anchor" href="#快速体验" aria-label="Permalink to &quot;快速体验&quot;">&ZeroWidthSpace;</a></h2>
<p>登录后您可以体验以下核心功能：</p>
<ul>
<li><strong>🏢 租户管理</strong> - 创建和管理多租户</li>
<li><strong>👥 用户管理</strong> - 用户、角色、权限配置</li>
<li><strong>📋 菜单管理</strong> - 动态菜单配置</li>
<li><strong>🔐 权限控制</strong> - RBAC 权限模型</li>
<li><strong>📊 系统监控</strong> - 日志、操作记录</li>
</ul>
<h2 id="遇到问题" tabindex="-1">遇到问题？ <a class="header-anchor" href="#遇到问题" aria-label="Permalink to &quot;遇到问题？&quot;">&ZeroWidthSpace;</a></h2>
<ul>
<li>📖 查看 <a href="/zh/faq/">常见问题</a></li>
<li>💬 加入技术交流群：789517089</li>
<li>🐛 提交 <a href="https://gitee.com/battcn/wemirr-platform/issues" target="_blank" rel="noreferrer">Issue</a></li>
</ul>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/quickstart/concept.html">核心概念</a> - 了解 SaaS、多租户等基础概念</li>
<li><a href="/zh/guide/docker/">Docker 部署</a> - 深入学习 Docker 部署方案</li>
<li><a href="/zh/guide/frontend/">前端开发</a> - 学习前端组件和页面开发</li>
<li><a href="/zh/guide/backend/">后端开发</a> - 学习后端 API 和服务开发</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[路由 ]]></title>
            <link>https://docs.battcn.com/zh/guide/router.html</link>
            <guid>https://docs.battcn.com/zh/guide/router.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="路由" tabindex="-1">路由 <a class="header-anchor" href="#路由" aria-label="Permalink to &quot;路由&quot;">&ZeroWidthSpace;</a></h1>
<p>路由配置存放于 src/router/routes 下面。<br>
src/router/routes/modules用于存放路由模块，路由模块会自动注册。</p>
<h2 id="路由模块" tabindex="-1">路由模块 <a class="header-anchor" href="#路由模块" aria-label="Permalink to &quot;路由模块&quot;">&ZeroWidthSpace;</a></h2>
<div class="info custom-block"><p class="custom-block-title">注意</p>
<p>在 src/router/routes/modules 内的 .ts 文件会被视为一个路由模块。</p>
</div>
<div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { LAYOUT } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '../basic'</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> dashboard</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RouteRecordItem</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/dashboard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Dashboard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  component: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">LAYOUT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  redirect: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/dashboard/analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  meta: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    orderNo: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ion:grid-outline'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.dashboard.dashboard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  children: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@/pages/dashboard/analysis/index.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      meta: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.dashboard.analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ion:grid-outline'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'workbench'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Workbench'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@/pages/dashboard/workbench/index.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      meta: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.dashboard.workbench'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ion:grid-outline'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dashboard</span></span></code></pre>
</div><h2 id="meta定义" tabindex="-1">meta定义 <a class="header-anchor" href="#meta定义" aria-label="Permalink to &quot;meta定义&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RouteMeta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 路由title  一般必填</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 动态路由可打开Tab页数</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  dynamicLevel</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 动态路由的实际Path, 即去除路由的动态部分;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  realPath</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否忽略权限，只在权限模式为Role的时候有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ignoreAuth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 可以访问的角色，只在权限模式为Role的时候有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  roles</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RoleEnum</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否忽略KeepAlive缓存</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ignoreKeepAlive</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否固定标签</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  affix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 图标，也是菜单图标</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  icon</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 内嵌iframe的地址</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  frameSrc</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 指定该路由切换的动画名</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  transitionName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 隐藏该路由在面包屑上面的显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideBreadcrumb</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 如果该路由会携带参数，且需要在tab页上面显示。则需要设置为true</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  carryParam</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 隐藏所有子菜单</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideChildrenInMenu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 当前激活的菜单。用于配置详情页时左侧激活的菜单路径</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  currentActiveMenu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 当前路由不再标签页显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideTab</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 当前路由不再菜单显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideMenu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 菜单排序，只对第一级有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  orderNo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 忽略路由。用于在ROUTE_MAPPING以及BACK权限模式下，生成对应的菜单而忽略路由。2.5.3以上版本有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ignoreRoute</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否在子级菜单的完整path中忽略本级path。2.5.3以上版本有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hidePathForChildren</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="动态路由tab自动关闭功能" tabindex="-1">动态路由Tab自动关闭功能 <a class="header-anchor" href="#动态路由tab自动关闭功能" aria-label="Permalink to &quot;动态路由Tab自动关闭功能&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-Ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">Ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'detail/:id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'TabDetail'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/@/views/demo/feat/tabs/TabDetail.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  meta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    currentActiveMenu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/feat/tabs'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">t</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.demo.feat.tabDetail'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    hideMenu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //dynamicLevel 最大能打开的Tab标签页数</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    dynamicLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //realPath 动态路由实际路径(考虑到动态路由有时候可能存在N层的情况</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //例：/:id/:subId/:...), 为了减少计算开销, </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //使用配置方式事先规定好路由的实际路径(注意: 该参数若不设置，将无法使用该功能)</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    realPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/feat/tabs/detail'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></description>
            <content:encoded><![CDATA[<h1 id="路由" tabindex="-1">路由 <a class="header-anchor" href="#路由" aria-label="Permalink to &quot;路由&quot;">&ZeroWidthSpace;</a></h1>
<p>路由配置存放于 src/router/routes 下面。<br>
src/router/routes/modules用于存放路由模块，路由模块会自动注册。</p>
<h2 id="路由模块" tabindex="-1">路由模块 <a class="header-anchor" href="#路由模块" aria-label="Permalink to &quot;路由模块&quot;">&ZeroWidthSpace;</a></h2>
<div class="info custom-block"><p class="custom-block-title">注意</p>
<p>在 src/router/routes/modules 内的 .ts 文件会被视为一个路由模块。</p>
</div>
<div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { LAYOUT } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '../basic'</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> dashboard</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RouteRecordItem</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/dashboard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Dashboard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  component: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">LAYOUT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  redirect: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/dashboard/analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  meta: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    orderNo: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ion:grid-outline'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.dashboard.dashboard'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  children: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@/pages/dashboard/analysis/index.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      meta: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.dashboard.analysis'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ion:grid-outline'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      path: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'workbench'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Workbench'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@/pages/dashboard/workbench/index.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      meta: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.dashboard.workbench'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        icon: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ion:grid-outline'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dashboard</span></span></code></pre>
</div><h2 id="meta定义" tabindex="-1">meta定义 <a class="header-anchor" href="#meta定义" aria-label="Permalink to &quot;meta定义&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RouteMeta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 路由title  一般必填</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 动态路由可打开Tab页数</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  dynamicLevel</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 动态路由的实际Path, 即去除路由的动态部分;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  realPath</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否忽略权限，只在权限模式为Role的时候有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ignoreAuth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 可以访问的角色，只在权限模式为Role的时候有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  roles</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> RoleEnum</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否忽略KeepAlive缓存</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ignoreKeepAlive</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否固定标签</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  affix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 图标，也是菜单图标</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  icon</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 内嵌iframe的地址</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  frameSrc</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 指定该路由切换的动画名</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  transitionName</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 隐藏该路由在面包屑上面的显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideBreadcrumb</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 如果该路由会携带参数，且需要在tab页上面显示。则需要设置为true</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  carryParam</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 隐藏所有子菜单</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideChildrenInMenu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 当前激活的菜单。用于配置详情页时左侧激活的菜单路径</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  currentActiveMenu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 当前路由不再标签页显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideTab</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 当前路由不再菜单显示</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hideMenu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 菜单排序，只对第一级有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  orderNo</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 忽略路由。用于在ROUTE_MAPPING以及BACK权限模式下，生成对应的菜单而忽略路由。2.5.3以上版本有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  ignoreRoute</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 是否在子级菜单的完整path中忽略本级path。2.5.3以上版本有效</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  hidePathForChildren</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="动态路由tab自动关闭功能" tabindex="-1">动态路由Tab自动关闭功能 <a class="header-anchor" href="#动态路由tab自动关闭功能" aria-label="Permalink to &quot;动态路由Tab自动关闭功能&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-Ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">Ts</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'detail/:id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'TabDetail'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  component</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/@/views/demo/feat/tabs/TabDetail.vue'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  meta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    currentActiveMenu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/feat/tabs'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    title</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">t</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'routes.demo.feat.tabDetail'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    hideMenu</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //dynamicLevel 最大能打开的Tab标签页数</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    dynamicLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //realPath 动态路由实际路径(考虑到动态路由有时候可能存在N层的情况</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //例：/:id/:subId/:...), 为了减少计算开销, </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    //使用配置方式事先规定好路由的实际路径(注意: 该参数若不设置，将无法使用该功能)</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    realPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/feat/tabs/detail'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SaaS 多租户概述 ]]></title>
            <link>https://docs.battcn.com/zh/guide/saas/</link>
            <guid>https://docs.battcn.com/zh/guide/saas/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="saas-多租户概述" tabindex="-1">SaaS 多租户概述 <a class="header-anchor" href="#saas-多租户概述" aria-label="Permalink to &quot;SaaS 多租户概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>深入理解 Wemirr Platform 的 SaaS 多租户架构设计和实现</p>
</div>
<h2 id="什么是多租户" tabindex="-1">什么是多租户？ <a class="header-anchor" href="#什么是多租户" aria-label="Permalink to &quot;什么是多租户？&quot;">&ZeroWidthSpace;</a></h2>
<p>多租户（Multi-Tenancy）是 SaaS 应用的核心架构模式，让多个租户（客户/组织）共享同一套应用程序，同时确保各租户数据完全隔离。</p>
<h3 id="核心特性" tabindex="-1">核心特性 <a class="header-anchor" href="#核心特性" aria-label="Permalink to &quot;核心特性&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│  💰 资源共享     │ 多租户共享计算资源，降低成本           │</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></code></pre>
</div><h2 id="架构设计" tabindex="-1">架构设计 <a class="header-anchor" href="#架构设计" aria-label="Permalink to &quot;架构设计&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="整体架构" tabindex="-1">整体架构 <a class="header-anchor" href="#整体架构" aria-label="Permalink to &quot;整体架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>                         ┌────────▼────────┐</span></span>
<span class="line"><span>                         │    API 网关      │</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>        │    IAM    │      │   Suite   │      │  Plugin   │</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>                      ├───────────────────────┤</span></span>
<span class="line"><span>                      │  ┌─────┐ ┌─────┐     │</span></span>
<span class="line"><span>                      │  │租户A│ │租户B│ ... │</span></span>
<span class="line"><span>                      │  └─────┘ └─────┘     │</span></span>
<span class="line"><span>                      └───────────────────────┘</span></span></code></pre>
</div><h3 id="租户识别" tabindex="-1">租户识别 <a class="header-anchor" href="#租户识别" aria-label="Permalink to &quot;租户识别&quot;">&ZeroWidthSpace;</a></h3>
<p>租户识别通过以下方式实现：</p>
<ol>
<li><strong>请求头</strong> - <code>X-Tenant-Id</code> 或 <code>Tenant-Code</code></li>
<li><strong>Token</strong> - JWT 中携带租户信息</li>
<li><strong>域名</strong> - 子域名解析</li>
</ol>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 租户解析优先级</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Header</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> X</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Code</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Token 中的 tenant_id 字段</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> 请求参数 tenantId</span></span></code></pre>
</div><h2 id="隔离策略" tabindex="-1">隔离策略 <a class="header-anchor" href="#隔离策略" aria-label="Permalink to &quot;隔离策略&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 支持三种租户隔离策略：</p>
<h3 id="_1-字段隔离-column" tabindex="-1">1. 字段隔离（Column） <a class="header-anchor" href="#_1-字段隔离-column" aria-label="Permalink to &quot;1. 字段隔离（Column）&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>原理</strong>：所有租户共享数据库和表，通过 <code>tenant_id</code> 字段区分数据。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌────────────────────────────────────────┐</span></span>
<span class="line"><span>│            t_order 表                   │</span></span>
<span class="line"><span>├────────────────────────────────────────┤</span></span>
<span class="line"><span>│  id  │ tenant_id │ order_no │  amount │</span></span>
<span class="line"><span>│  1   │    1001   │ NO001    │  100.00 │</span></span>
<span class="line"><span>│  2   │    1001   │ NO002    │  200.00 │</span></span>
<span class="line"><span>│  3   │    1002   │ NO001    │  150.00 │</span></span>
<span class="line"><span>│  4   │    1002   │ NO002    │  300.00 │</span></span>
<span class="line"><span>└────────────────────────────────────────┘</span></span></code></pre>
</div><p><strong>配置方式</strong>：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">column</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      include-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_order,t_product,t_customer</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 忽略租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      ignore-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_dict,sys_config</span></span></code></pre>
</div><p><strong>特点</strong>：</p>
<table tabindex="0">
<thead>
<tr>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>简单易用，开发成本低</td>
<td>单表数据量大时性能下降</td>
</tr>
<tr>
<td>便于统计分析</td>
<td>安全风险：SQL 注入可能跨租户</td>
</tr>
<tr>
<td>资源利用率高</td>
<td>租户定制化受限</td>
</tr>
</tbody>
</table>
<p><strong>适用场景</strong>：中小型租户，年数据量 &lt; 200W</p>
<h3 id="_2-schema-隔离" tabindex="-1">2. Schema 隔离 <a class="header-anchor" href="#_2-schema-隔离" aria-label="Permalink to &quot;2. Schema 隔离&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>原理</strong>：每个租户独立 Schema（数据库），共享数据库实例。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────┐</span></span>
<span class="line"><span>│           MySQL 实例                     │</span></span>
<span class="line"><span>├─────────────┬─────────────┬─────────────┤</span></span>
<span class="line"><span>│  tenant_001 │  tenant_002 │  tenant_003 │</span></span>
<span class="line"><span>│  ├─ t_order │  ├─ t_order │  ├─ t_order │</span></span>
<span class="line"><span>│  ├─ t_user  │  ├─ t_user  │  ├─ t_user  │</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>
</tr>
</thead>
<tbody>
<tr>
<td>数据隔离性好</td>
<td>租户数量受数据库实例限制</td>
</tr>
<tr>
<td>便于备份恢复</td>
<td>跨租户统计复杂</td>
</tr>
<tr>
<td>支持一定定制化</td>
<td>Schema 管理复杂</td>
</tr>
</tbody>
</table>
<h3 id="_3-数据源隔离-datasource" tabindex="-1">3. 数据源隔离（DataSource） <a class="header-anchor" href="#_3-数据源隔离-datasource" aria-label="Permalink to &quot;3. 数据源隔离（DataSource）&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>原理</strong>：每个租户独立数据库实例，完全物理隔离。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐  ┌─────────────┐  ┌─────────────┐</span></span>
<span class="line"><span>│  MySQL-A    │  │  MySQL-B    │  │  MySQL-C    │</span></span>
<span class="line"><span>│  (租户 A)   │  │  (租户 B)   │  │  (租户 C)   │</span></span>
<span class="line"><span>│  ├─ t_order │  │  ├─ t_order │  │  ├─ t_order │</span></span>
<span class="line"><span>│  └─ ...     │  │  └─ ...     │  │  └─ ...     │</span></span>
<span class="line"><span>└─────────────┘  └─────────────┘  └─────────────┘</span></span></code></pre>
</div><p><strong>配置方式</strong>：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">datasource</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">feign</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 非 IAM 模块配置为 feign</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      db-notify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> # 数据源变更通知方式</span></span></code></pre>
</div><p><strong>特点</strong>：</p>
<table tabindex="0">
<thead>
<tr>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>完全隔离，安全性最高</td>
<td>成本高</td>
</tr>
<tr>
<td>性能最优</td>
<td>运维复杂</td>
</tr>
<tr>
<td>支持完全定制化</td>
<td>统一升级复杂</td>
</tr>
</tbody>
</table>
<p><strong>适用场景</strong>：大型租户，高安全要求，年数据量 &gt; 200W</p>
<h2 id="选择建议" tabindex="-1">选择建议 <a class="header-anchor" href="#选择建议" aria-label="Permalink to &quot;选择建议&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>                    租户数据量大吗？</span></span>
<span class="line"><span>                    年订单 > 200W？</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>       ┌──────┴──────┐               ┌────────┴────────┐</span></span>
<span class="line"><span>       │             │               │                 │</span></span>
<span class="line"><span>      是            否              是                否</span></span>
<span class="line"><span>       │             │               │                 │</span></span>
<span class="line"><span>   字段隔离      Schema隔离      数据源隔离        Schema隔离</span></span></code></pre>
</div><h2 id="核心功能" tabindex="-1">核心功能 <a class="header-anchor" href="#核心功能" aria-label="Permalink to &quot;核心功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="租户管理" tabindex="-1">租户管理 <a class="header-anchor" href="#租户管理" aria-label="Permalink to &quot;租户管理&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>租户创建</strong> - 创建新租户，初始化基础数据</li>
<li><strong>租户配置</strong> - 配置租户功能、限制、外观</li>
<li><strong>租户切换</strong> - 支持用户在多租户间切换</li>
<li><strong>租户禁用</strong> - 暂停或禁用租户</li>
</ul>
<h3 id="产品订阅" tabindex="-1">产品订阅 <a class="header-anchor" href="#产品订阅" aria-label="Permalink to &quot;产品订阅&quot;">&ZeroWidthSpace;</a></h3>
<p>支持租户订阅不同产品套餐：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│  ├─ 5 用户       │  ├─ 50 用户      │  ├─ 不限用户    │</span></span>
<span class="line"><span>│  ├─ 1GB 存储     │  ├─ 100GB 存储   │  ├─ 1TB 存储    │</span></span>
<span class="line"><span>│  ├─ 基础功能     │  ├─ 高级功能     │  ├─ 全部功能    │</span></span>
<span class="line"><span>│  └─ ¥99/月      │  └─ ¥999/月     │  └─ ¥9999/月   │</span></span>
<span class="line"><span>└──────────────────────────────────────────────────────┘</span></span></code></pre>
</div><h3 id="数据初始化" tabindex="-1">数据初始化 <a class="header-anchor" href="#数据初始化" aria-label="Permalink to &quot;数据初始化&quot;">&ZeroWidthSpace;</a></h3>
<p>新租户创建时自动初始化：</p>
<ul>
<li>基础角色和权限</li>
<li>系统字典数据</li>
<li>默认管理员账号</li>
<li>初始配置项</li>
</ul>
<h2 id="真实代码示例" tabindex="-1">真实代码示例 <a class="header-anchor" href="#真实代码示例" aria-label="Permalink to &quot;真实代码示例&quot;">&ZeroWidthSpace;</a></h2>
<p>以下代码均来自 <code>wemirr-platform-iam</code> 模块的真实实现。</p>
<h3 id="租户实体" tabindex="-1">租户实体 <a class="header-anchor" href="#租户实体" aria-label="Permalink to &quot;租户实体&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 Tenant.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_tenant"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String code;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String name;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态;0=未启用;1=启用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "LOGO"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String logo;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "联系人"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String contactPerson;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "联系方式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String contactPhone;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "行业"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String industry;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "统一信用代码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String creditCode;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "法人"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String legalPersonName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="租户数据源绑定" tabindex="-1">租户数据源绑定 <a class="header-anchor" href="#租户数据源绑定" aria-label="Permalink to &quot;租户数据源绑定&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantDbBinding.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_tenant_db_binding"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantDbBinding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "物理节点ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long dbInstanceId;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "隔离策略: DATABASE, SCHEMA, COLUMN"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String strategy;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "运行时Schema名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String schemaName;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "是否为主数据源"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean isPrimary;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="租户创建" tabindex="-1">租户创建 <a class="header-anchor" href="#租户创建" aria-label="Permalink to &quot;租户创建&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantSaveReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 1. 校验租户名称唯一</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户名称重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 2. 校验租户编码唯一</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCode, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户编码重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 3. 创建租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, Tenant.class);</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="租户初始化" tabindex="-1">租户初始化 <a class="header-anchor" href="#租户初始化" aria-label="Permalink to &quot;租户初始化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java - 初始化租户数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initSqlScript</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    var</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户信息不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户未启用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DatabaseProperties.MultiTenant multiTenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> properties.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMultiTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant, multiTenant)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"超级租户,禁止操作"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 根据隔离模式选择初始化方式</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultiTenantType.COLUMN) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        initColumnTypeTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段隔离</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultiTenantType.DATASOURCE) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        initDatasourceTypeTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据源隔离</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 初始化租户基础数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initializeTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant tenant, Role role) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 1. 创建默认组织</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Org org </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Org</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setLabel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.orgMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(org);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 2. 创建管理员用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContactPhone</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PasswordEncoderHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">encode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"123456"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setNickName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContactPerson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 3. 分配管理员角色</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userRoleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserRole.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">roleId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="tenanthelper-工具类" tabindex="-1">TenantHelper 工具类 <a class="header-anchor" href="#tenanthelper-工具类" aria-label="Permalink to &quot;TenantHelper 工具类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantHelper.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 判断是否是超级租户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> isSuper </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用主数据源执行（查询平台级数据）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> dictList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithMaster</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(SysDict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getType, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换到指定租户数据源执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8888"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 临时忽略租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">withIgnoreStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据隔离类型执行不同逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithIsolationType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dbSupplier.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(),     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据源隔离逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> columnSupplier.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段隔离逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><hr>
<h2 id="租户管理-api" tabindex="-1">租户管理 API <a class="header-anchor" href="#租户管理-api" aria-label="Permalink to &quot;租户管理 API&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "创建租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "修改租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/init-sql-script"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "加载初始数据"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "tenant:init-script"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initSqlScript</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/db-ref"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户关联DB"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantDbBindingResp </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dbRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/db-binding"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户关联DB"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dbBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantDbBindingSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/refresh-dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "字典刷新"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> refreshTenantDict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="数据源隔离模式详解" tabindex="-1">数据源隔离模式详解 <a class="header-anchor" href="#数据源隔离模式详解" aria-label="Permalink to &quot;数据源隔离模式详解&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java - 数据源隔离初始化</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initDatasourceTypeTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant tenant) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DynamicDataSourceHandler handler </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SpringUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DynamicDataSourceHandler.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 准备变量</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> variables </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Maps.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newHashMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    variables.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"tenant_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    variables.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"tenant_name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 获取租户绑定的数据源</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DbInstancePageResp dbInstance </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dbInstanceMapper</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantDynamicDatasourceByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 执行初始化SQL脚本</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DynamicDataSourceEvent event </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dbInstance, DynamicDataSourceEvent.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    handler.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">initSqlScript</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event, variables);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 切换到租户数据源初始化数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        initializeTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant, role);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/saas/tenant.html">租户配置</a> - 学习租户配置详解</li>
<li><a href="/zh/guide/saas/permission.html">数据权限</a> - 了解数据权限控制</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="saas-多租户概述" tabindex="-1">SaaS 多租户概述 <a class="header-anchor" href="#saas-多租户概述" aria-label="Permalink to &quot;SaaS 多租户概述&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>深入理解 Wemirr Platform 的 SaaS 多租户架构设计和实现</p>
</div>
<h2 id="什么是多租户" tabindex="-1">什么是多租户？ <a class="header-anchor" href="#什么是多租户" aria-label="Permalink to &quot;什么是多租户？&quot;">&ZeroWidthSpace;</a></h2>
<p>多租户（Multi-Tenancy）是 SaaS 应用的核心架构模式，让多个租户（客户/组织）共享同一套应用程序，同时确保各租户数据完全隔离。</p>
<h3 id="核心特性" tabindex="-1">核心特性 <a class="header-anchor" href="#核心特性" aria-label="Permalink to &quot;核心特性&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│  💰 资源共享     │ 多租户共享计算资源，降低成本           │</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></code></pre>
</div><h2 id="架构设计" tabindex="-1">架构设计 <a class="header-anchor" href="#架构设计" aria-label="Permalink to &quot;架构设计&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="整体架构" tabindex="-1">整体架构 <a class="header-anchor" href="#整体架构" aria-label="Permalink to &quot;整体架构&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>                         ┌────────▼────────┐</span></span>
<span class="line"><span>                         │    API 网关      │</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>        │    IAM    │      │   Suite   │      │  Plugin   │</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>                      ├───────────────────────┤</span></span>
<span class="line"><span>                      │  ┌─────┐ ┌─────┐     │</span></span>
<span class="line"><span>                      │  │租户A│ │租户B│ ... │</span></span>
<span class="line"><span>                      │  └─────┘ └─────┘     │</span></span>
<span class="line"><span>                      └───────────────────────┘</span></span></code></pre>
</div><h3 id="租户识别" tabindex="-1">租户识别 <a class="header-anchor" href="#租户识别" aria-label="Permalink to &quot;租户识别&quot;">&ZeroWidthSpace;</a></h3>
<p>租户识别通过以下方式实现：</p>
<ol>
<li><strong>请求头</strong> - <code>X-Tenant-Id</code> 或 <code>Tenant-Code</code></li>
<li><strong>Token</strong> - JWT 中携带租户信息</li>
<li><strong>域名</strong> - 子域名解析</li>
</ol>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 租户解析优先级</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Header</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> X</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Code</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Token 中的 tenant_id 字段</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> 请求参数 tenantId</span></span></code></pre>
</div><h2 id="隔离策略" tabindex="-1">隔离策略 <a class="header-anchor" href="#隔离策略" aria-label="Permalink to &quot;隔离策略&quot;">&ZeroWidthSpace;</a></h2>
<p>Wemirr Platform 支持三种租户隔离策略：</p>
<h3 id="_1-字段隔离-column" tabindex="-1">1. 字段隔离（Column） <a class="header-anchor" href="#_1-字段隔离-column" aria-label="Permalink to &quot;1. 字段隔离（Column）&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>原理</strong>：所有租户共享数据库和表，通过 <code>tenant_id</code> 字段区分数据。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌────────────────────────────────────────┐</span></span>
<span class="line"><span>│            t_order 表                   │</span></span>
<span class="line"><span>├────────────────────────────────────────┤</span></span>
<span class="line"><span>│  id  │ tenant_id │ order_no │  amount │</span></span>
<span class="line"><span>│  1   │    1001   │ NO001    │  100.00 │</span></span>
<span class="line"><span>│  2   │    1001   │ NO002    │  200.00 │</span></span>
<span class="line"><span>│  3   │    1002   │ NO001    │  150.00 │</span></span>
<span class="line"><span>│  4   │    1002   │ NO002    │  300.00 │</span></span>
<span class="line"><span>└────────────────────────────────────────┘</span></span></code></pre>
</div><p><strong>配置方式</strong>：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">column</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      include-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_order,t_product,t_customer</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # 忽略租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      ignore-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_dict,sys_config</span></span></code></pre>
</div><p><strong>特点</strong>：</p>
<table tabindex="0">
<thead>
<tr>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>简单易用，开发成本低</td>
<td>单表数据量大时性能下降</td>
</tr>
<tr>
<td>便于统计分析</td>
<td>安全风险：SQL 注入可能跨租户</td>
</tr>
<tr>
<td>资源利用率高</td>
<td>租户定制化受限</td>
</tr>
</tbody>
</table>
<p><strong>适用场景</strong>：中小型租户，年数据量 &lt; 200W</p>
<h3 id="_2-schema-隔离" tabindex="-1">2. Schema 隔离 <a class="header-anchor" href="#_2-schema-隔离" aria-label="Permalink to &quot;2. Schema 隔离&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>原理</strong>：每个租户独立 Schema（数据库），共享数据库实例。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────────────────────────────────┐</span></span>
<span class="line"><span>│           MySQL 实例                     │</span></span>
<span class="line"><span>├─────────────┬─────────────┬─────────────┤</span></span>
<span class="line"><span>│  tenant_001 │  tenant_002 │  tenant_003 │</span></span>
<span class="line"><span>│  ├─ t_order │  ├─ t_order │  ├─ t_order │</span></span>
<span class="line"><span>│  ├─ t_user  │  ├─ t_user  │  ├─ t_user  │</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>
</tr>
</thead>
<tbody>
<tr>
<td>数据隔离性好</td>
<td>租户数量受数据库实例限制</td>
</tr>
<tr>
<td>便于备份恢复</td>
<td>跨租户统计复杂</td>
</tr>
<tr>
<td>支持一定定制化</td>
<td>Schema 管理复杂</td>
</tr>
</tbody>
</table>
<h3 id="_3-数据源隔离-datasource" tabindex="-1">3. 数据源隔离（DataSource） <a class="header-anchor" href="#_3-数据源隔离-datasource" aria-label="Permalink to &quot;3. 数据源隔离（DataSource）&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>原理</strong>：每个租户独立数据库实例，完全物理隔离。</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>┌─────────────┐  ┌─────────────┐  ┌─────────────┐</span></span>
<span class="line"><span>│  MySQL-A    │  │  MySQL-B    │  │  MySQL-C    │</span></span>
<span class="line"><span>│  (租户 A)   │  │  (租户 B)   │  │  (租户 C)   │</span></span>
<span class="line"><span>│  ├─ t_order │  │  ├─ t_order │  │  ├─ t_order │</span></span>
<span class="line"><span>│  └─ ...     │  │  └─ ...     │  │  └─ ...     │</span></span>
<span class="line"><span>└─────────────┘  └─────────────┘  └─────────────┘</span></span></code></pre>
</div><p><strong>配置方式</strong>：</p>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">datasource</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">feign</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  # 非 IAM 模块配置为 feign</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      db-notify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> # 数据源变更通知方式</span></span></code></pre>
</div><p><strong>特点</strong>：</p>
<table tabindex="0">
<thead>
<tr>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>完全隔离，安全性最高</td>
<td>成本高</td>
</tr>
<tr>
<td>性能最优</td>
<td>运维复杂</td>
</tr>
<tr>
<td>支持完全定制化</td>
<td>统一升级复杂</td>
</tr>
</tbody>
</table>
<p><strong>适用场景</strong>：大型租户，高安全要求，年数据量 &gt; 200W</p>
<h2 id="选择建议" tabindex="-1">选择建议 <a class="header-anchor" href="#选择建议" aria-label="Permalink to &quot;选择建议&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>                    租户数据量大吗？</span></span>
<span class="line"><span>                    年订单 > 200W？</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>       ┌──────┴──────┐               ┌────────┴────────┐</span></span>
<span class="line"><span>       │             │               │                 │</span></span>
<span class="line"><span>      是            否              是                否</span></span>
<span class="line"><span>       │             │               │                 │</span></span>
<span class="line"><span>   字段隔离      Schema隔离      数据源隔离        Schema隔离</span></span></code></pre>
</div><h2 id="核心功能" tabindex="-1">核心功能 <a class="header-anchor" href="#核心功能" aria-label="Permalink to &quot;核心功能&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="租户管理" tabindex="-1">租户管理 <a class="header-anchor" href="#租户管理" aria-label="Permalink to &quot;租户管理&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li><strong>租户创建</strong> - 创建新租户，初始化基础数据</li>
<li><strong>租户配置</strong> - 配置租户功能、限制、外观</li>
<li><strong>租户切换</strong> - 支持用户在多租户间切换</li>
<li><strong>租户禁用</strong> - 暂停或禁用租户</li>
</ul>
<h3 id="产品订阅" tabindex="-1">产品订阅 <a class="header-anchor" href="#产品订阅" aria-label="Permalink to &quot;产品订阅&quot;">&ZeroWidthSpace;</a></h3>
<p>支持租户订阅不同产品套餐：</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>│  ├─ 5 用户       │  ├─ 50 用户      │  ├─ 不限用户    │</span></span>
<span class="line"><span>│  ├─ 1GB 存储     │  ├─ 100GB 存储   │  ├─ 1TB 存储    │</span></span>
<span class="line"><span>│  ├─ 基础功能     │  ├─ 高级功能     │  ├─ 全部功能    │</span></span>
<span class="line"><span>│  └─ ¥99/月      │  └─ ¥999/月     │  └─ ¥9999/月   │</span></span>
<span class="line"><span>└──────────────────────────────────────────────────────┘</span></span></code></pre>
</div><h3 id="数据初始化" tabindex="-1">数据初始化 <a class="header-anchor" href="#数据初始化" aria-label="Permalink to &quot;数据初始化&quot;">&ZeroWidthSpace;</a></h3>
<p>新租户创建时自动初始化：</p>
<ul>
<li>基础角色和权限</li>
<li>系统字典数据</li>
<li>默认管理员账号</li>
<li>初始配置项</li>
</ul>
<h2 id="真实代码示例" tabindex="-1">真实代码示例 <a class="header-anchor" href="#真实代码示例" aria-label="Permalink to &quot;真实代码示例&quot;">&ZeroWidthSpace;</a></h2>
<p>以下代码均来自 <code>wemirr-platform-iam</code> 模块的真实实现。</p>
<h3 id="租户实体" tabindex="-1">租户实体 <a class="header-anchor" href="#租户实体" aria-label="Permalink to &quot;租户实体&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 Tenant.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_tenant"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "编码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String code;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String name;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "状态;0=未启用;1=启用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean status;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "LOGO"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String logo;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "联系人"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String contactPerson;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "联系方式"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String contactPhone;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "行业"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String industry;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "统一信用代码"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String creditCode;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "法人"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String legalPersonName;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="租户数据源绑定" tabindex="-1">租户数据源绑定 <a class="header-anchor" href="#租户数据源绑定" aria-label="Permalink to &quot;租户数据源绑定&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantDbBinding.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TableName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"t_tenant_db_binding"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantDbBinding</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> SuperEntity</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "物理节点ID"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long dbInstanceId;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "隔离策略: DATABASE, SCHEMA, COLUMN"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String strategy;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "运行时Schema名称"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String schemaName;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Schema</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "是否为主数据源"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Boolean isPrimary;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="租户创建" tabindex="-1">租户创建 <a class="header-anchor" href="#租户创建" aria-label="Permalink to &quot;租户创建&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantSaveReq req) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 1. 校验租户名称唯一</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getName, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (nameCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户名称重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 2. 校验租户编码唯一</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCode, req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (codeCount </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户编码重复"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 3. 创建租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req, Tenant.class);</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="租户初始化" tabindex="-1">租户初始化 <a class="header-anchor" href="#租户初始化" aria-label="Permalink to &quot;租户初始化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java - 初始化租户数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DSTransactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initSqlScript</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long id) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    var</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Optional.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ofNullable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.baseMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElseThrow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">notFound</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户信息不存在"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"租户未启用"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DatabaseProperties.MultiTenant multiTenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> properties.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMultiTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant, multiTenant)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CheckedException.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">badRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"超级租户,禁止操作"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 根据隔离模式选择初始化方式</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultiTenantType.COLUMN) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        initColumnTypeTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段隔离</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> MultiTenantType.DATASOURCE) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        initDatasourceTypeTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据源隔离</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 初始化租户基础数据</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initializeTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant tenant, Role role) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 1. 创建默认组织</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Org org </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Org</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setLabel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.orgMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(org);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 2. 创建管理员用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setUsername</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContactPhone</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setPassword</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(PasswordEncoderHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">encode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"123456"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setNickName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContactPerson</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 3. 分配管理员角色</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.userRoleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(UserRole.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(user.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">roleId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="tenanthelper-工具类" tabindex="-1">TenantHelper 工具类 <a class="header-anchor" href="#tenanthelper-工具类" aria-label="Permalink to &quot;TenantHelper 工具类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantHelper.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 判断是否是超级租户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> isSuper </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isSuperTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用主数据源执行（查询平台级数据）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Dict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> dictList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithMaster</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dictMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(SysDict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getType, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换到指定租户数据源执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"8888"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">User</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> users </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> users;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 临时忽略租户过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User user </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">withIgnoreStrategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 根据隔离类型执行不同逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithIsolationType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dbSupplier.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(),     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 数据源隔离逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> columnSupplier.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段隔离逻辑</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><hr>
<h2 id="租户管理-api" tabindex="-1">租户管理 API <a class="header-anchor" href="#租户管理-api" aria-label="Permalink to &quot;租户管理 API&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantController.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RestController</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tag</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户管理"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantController</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "创建租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "修改租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> modify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/init-sql-script"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "加载初始数据"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RedisLock</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">prefix</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "tenant:init-script"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initSqlScript</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/db-ref"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户关联DB"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantDbBindingResp </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dbRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/db-binding"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "租户关联DB"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> dbBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantDbBindingSaveReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PutMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/{id}/refresh-dict"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Operation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">summary</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "字典刷新"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> refreshTenantDict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="数据源隔离模式详解" tabindex="-1">数据源隔离模式详解 <a class="header-anchor" href="#数据源隔离模式详解" aria-label="Permalink to &quot;数据源隔离模式详解&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 TenantServiceImpl.java - 数据源隔离初始化</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initDatasourceTypeTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Tenant tenant) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DynamicDataSourceHandler handler </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SpringUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DynamicDataSourceHandler.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 准备变量</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Map&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Object</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> variables </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Maps.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">newHashMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    variables.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"tenant_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    variables.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"tenant_name"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getName</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 获取租户绑定的数据源</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DbInstancePageResp dbInstance </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dbInstanceMapper</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantDynamicDatasourceByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 执行初始化SQL脚本</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DynamicDataSourceEvent event </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toBean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dbInstance, DynamicDataSourceEvent.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    handler.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">initSqlScript</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event, variables);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 切换到租户数据源初始化数据</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    TenantHelper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithTenantDb</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        initializeTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant, role);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/saas/tenant.html">租户配置</a> - 学习租户配置详解</li>
<li><a href="/zh/guide/saas/permission.html">数据权限</a> - 了解数据权限控制</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[数据权限控制 ]]></title>
            <link>https://docs.battcn.com/zh/guide/saas/permission.html</link>
            <guid>https://docs.battcn.com/zh/guide/saas/permission.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="数据权限控制" tabindex="-1">数据权限控制 <a class="header-anchor" href="#数据权限控制" aria-label="Permalink to &quot;数据权限控制&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 数据权限的设计原理和使用方法</p>
</div>
<h2 id="什么是数据权限" tabindex="-1">什么是数据权限？ <a class="header-anchor" href="#什么是数据权限" aria-label="Permalink to &quot;什么是数据权限？&quot;">&ZeroWidthSpace;</a></h2>
<p>数据权限是在功能权限（能否访问某个菜单/按钮）基础上，进一步控制用户<strong>能看到哪些数据</strong>。</p>
<h3 id="权限层级" tabindex="-1">权限层级 <a class="header-anchor" href="#权限层级" aria-label="Permalink to &quot;权限层级&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>├─────────────────────────────────────────────────────────┤</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></code></pre>
</div><h2 id="数据权限类型-真实代码" tabindex="-1">数据权限类型（真实代码） <a class="header-anchor" href="#数据权限类型-真实代码" aria-label="Permalink to &quot;数据权限类型（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeType.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Getter</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AllArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeType</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IEnum</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Integer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    ALL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"全部"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),                    </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 查看所有数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL_CHILDREN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"本级以及子级"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 本部门及下级</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"本级"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 仅本部门</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    CUSTOMIZE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"自定义"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义范围</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SELF</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"个人"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),                    </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 仅本人</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    IGNORE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"跟随系统上下文"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 跟随系统</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer type;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String desc;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p><strong>值越大，权限越大</strong>：<code>ALL(50) &gt; THIS_LEVEL_CHILDREN(40) &gt; THIS_LEVEL(30) &gt; CUSTOMIZE(20) &gt; SELF(10)</code></p>
<hr>
<h2 id="核心-api-真实代码" tabindex="-1">核心 API（真实代码） <a class="header-anchor" href="#核心-api-真实代码" aria-label="Permalink to &quot;核心 API（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="datascope-注解" tabindex="-1">@DataScope 注解 <a class="header-anchor" href="#datascope-注解" aria-label="Permalink to &quot;@DataScope 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScope.java - 标记在 Mapper 类或方法上</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ElementType.TYPE, ElementType.METHOD})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Retention</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RetentionPolicy.RUNTIME)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 是否忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 数据权限字段配置</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {};</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="datacolumn-注解" tabindex="-1">@DataColumn 注解 <a class="header-anchor" href="#datacolumn-注解" aria-label="Permalink to &quot;@DataColumn 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataColumn.java - 定义数据权限字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ElementType.METHOD})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Retention</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RetentionPolicy.RUNTIME)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 表别名（用于多表关联查询）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">alias</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ""</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 字段名称（默认 created_by）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Entity.CREATE_USER_COLUMN;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * Java 类型</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;?></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> javaClass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long.class;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 权限资源类型（默认 USER）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DataResourceType </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 权限范围（默认跟随系统）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DataScopeType </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.IGNORE;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="注解方式使用" tabindex="-1">注解方式使用 <a class="header-anchor" href="#注解方式使用" aria-label="Permalink to &quot;注解方式使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="在-mapper-上使用" tabindex="-1">在 Mapper 上使用 <a class="header-anchor" href="#在-mapper-上使用" aria-label="Permalink to &quot;在 Mapper 上使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderMapper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BaseMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 使用默认字段 created_by 进行数据权限过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 指定表别名和字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectListWithDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 多字段数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "org_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.ORG)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectComplexPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">ignore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByIdIgnorePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="编程方式使用-真实代码" tabindex="-1">编程方式使用（真实代码） <a class="header-anchor" href="#编程方式使用-真实代码" aria-label="Permalink to &quot;编程方式使用（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="datapermissionruleholder-api" tabindex="-1">DataPermissionRuleHolder API <a class="header-anchor" href="#datapermissionruleholder-api" aria-label="Permalink to &quot;DataPermissionRuleHolder API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataPermissionRuleHolder.java - 编程式数据权限控制</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataPermissionRuleHolder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 获取当前的 DataPermissionRule</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">peek</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 入栈一个 DataPermissionRule</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataPermissionRule </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 弹出最顶部 DataPermissionRule</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> poll</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 清除 ThreadLocal</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> clear</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="datapermissionutils-工具类" tabindex="-1">DataPermissionUtils 工具类 <a class="header-anchor" href="#datapermissionutils-工具类" aria-label="Permalink to &quot;DataPermissionUtils 工具类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataPermissionUtils.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用默认规则执行（created_by 字段）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orders </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeDefaultDataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用指定规则执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule.Column.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">alias</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(rule, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), wrapper);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DataPermissionRule ignoreRule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ignoreRule, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h3 id="datapermissionrule-规则类" tabindex="-1">DataPermissionRule 规则类 <a class="header-anchor" href="#datapermissionrule-规则类" aria-label="Permalink to &quot;DataPermissionRule 规则类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataPermissionRule.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 是否忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ignore;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 规则字段列表</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> columns;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String alias;                              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 表别名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Entity.CREATE_USER_COLUMN;   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段名（默认 created_by）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Class&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> javaClass </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long.class;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Java 类型</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType resource </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 资源类型</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType scopeType </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.IGNORE;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 权限范围</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="数据权限实现原理" tabindex="-1">数据权限实现原理 <a class="header-anchor" href="#数据权限实现原理" aria-label="Permalink to &quot;数据权限实现原理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="datascopepermissionhandler" tabindex="-1">DataScopePermissionHandler <a class="header-anchor" href="#datascopepermissionhandler" aria-label="Permalink to &quot;DataScopePermissionHandler&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopePermissionHandler.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopePermissionHandler</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MultiDataPermissionHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getSqlSegment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Expression </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 1. 匿名用户不处理</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">anonymous</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 2. 优先从 DataPermissionRuleHolder 获取规则（编程式）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRuleHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">peek</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 3. 其次从注解获取规则</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionRuleByMappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mappedStatementId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 4. 构建 SQL 条件</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(table, rule);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, DataPermissionRule </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isIgnore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 全部权限不过滤</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.ALL) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 构建条件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Expression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> conditions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildConditions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            context, table, rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getColumns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> conditions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reduce</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AndExpression</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="数据权限服务实现" tabindex="-1">数据权限服务实现 <a class="header-anchor" href="#数据权限服务实现" aria-label="Permalink to &quot;数据权限服务实现&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RoleMapper roleMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRefMapper dataPermissionRefMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserMapper userMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrgService orgService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataScopeById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orgId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Role</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">findRoleByUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (CollectionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 找到权限最大的角色（值越大权限越大）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Role role </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Comparator.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">comparingInt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CUSTOMIZE) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 自定义：查询角色关联的组织下的用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dataPermissionRefMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(...)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataPermissionRef</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getDataId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 本级：同组织用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgId))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL_CHILDREN) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 本级及子级</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orgService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFullTreeIdPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orgId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER, userIdList);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="实战示例" tabindex="-1">实战示例 <a class="header-anchor" href="#实战示例" aria-label="Permalink to &quot;实战示例&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="service-层使用" tabindex="-1">Service 层使用 <a class="header-anchor" href="#service-层使用" aria-label="Permalink to &quot;Service 层使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderMapper orderMapper;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 方式一：依赖 Mapper 上的 @DataScope 注解</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 方式二：编程式控制</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageListWithCustomRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                DataPermissionRule.Column.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            ))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(rule, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orderByDesc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCreatedTime));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 方式三：临时忽略数据权限（管理员操作）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByIdForAdmin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="多表关联查询" tabindex="-1">多表关联查询 <a class="header-anchor" href="#多表关联查询" aria-label="Permalink to &quot;多表关联查询&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderMapper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BaseMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // XML 中的 SQL：SELECT o.*, u.nick_name FROM t_order o LEFT JOIN t_user u ON o.created_by = u.id</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPageWithUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="配置说明" tabindex="-1">配置说明 <a class="header-anchor" href="#配置说明" aria-label="Permalink to &quot;配置说明&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    data-permission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 启用数据权限</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      remote</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 是否远程获取权限（对时效性要求高时开启）</span></span></code></pre>
</div><hr>
<h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-优先级" tabindex="-1">1. 优先级 <a class="header-anchor" href="#_1-优先级" aria-label="Permalink to &quot;1. 优先级&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>DataPermissionRuleHolder（编程式） > @DataScope 注解</span></span></code></pre>
</div><p>编程式控制优先级最高，可在运行时动态覆盖注解配置。</p>
<h3 id="_2-字段规范" tabindex="-1">2. 字段规范 <a class="header-anchor" href="#_2-字段规范" aria-label="Permalink to &quot;2. 字段规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> t_order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    -- 业务字段...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    -- 数据权限字段（必须）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    created_by </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">bigint</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建人ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">bigint</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'所属组织ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    -- 审计字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    created_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">datetime</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    last_modify_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">datetime</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 添加索引</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_created_by</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(created_by);</span></span></code></pre>
</div><h3 id="_3-常见问题" tabindex="-1">3. 常见问题 <a class="header-anchor" href="#_3-常见问题" aria-label="Permalink to &quot;3. 常见问题&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>Q: 多角色如何处理？</strong>
A: 取权限最大的角色（<code>scopeType</code> 值最大）。</p>
<p><strong>Q: 如何临时忽略数据权限？</strong>
A: 使用 <code>@DataScope(ignore = true)</code> 或 <code>DataPermissionUtils.executeWithRule(ignoreRule, ...)</code>。</p>
<hr>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/advanced/">进阶开发</a> - 学习进阶开发技巧</li>
<li><a href="/zh/guide/advanced/performance.html">性能优化</a> - 了解性能优化方案</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="数据权限控制" tabindex="-1">数据权限控制 <a class="header-anchor" href="#数据权限控制" aria-label="Permalink to &quot;数据权限控制&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 数据权限的设计原理和使用方法</p>
</div>
<h2 id="什么是数据权限" tabindex="-1">什么是数据权限？ <a class="header-anchor" href="#什么是数据权限" aria-label="Permalink to &quot;什么是数据权限？&quot;">&ZeroWidthSpace;</a></h2>
<p>数据权限是在功能权限（能否访问某个菜单/按钮）基础上，进一步控制用户<strong>能看到哪些数据</strong>。</p>
<h3 id="权限层级" tabindex="-1">权限层级 <a class="header-anchor" href="#权限层级" aria-label="Permalink to &quot;权限层级&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>├─────────────────────────────────────────────────────────┤</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></code></pre>
</div><h2 id="数据权限类型-真实代码" tabindex="-1">数据权限类型（真实代码） <a class="header-anchor" href="#数据权限类型-真实代码" aria-label="Permalink to &quot;数据权限类型（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeType.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Getter</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">AllArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> enum</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeType</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IEnum</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Integer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    ALL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"全部"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),                    </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 查看所有数据</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL_CHILDREN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">40</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"本级以及子级"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 本部门及下级</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    THIS_LEVEL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">30</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"本级"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 仅本部门</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    CUSTOMIZE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">20</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"自定义"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),             </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 自定义范围</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    SELF</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">10</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"个人"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),                    </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 仅本人</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    IGNORE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"跟随系统上下文"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 跟随系统</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer type;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String desc;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p><strong>值越大，权限越大</strong>：<code>ALL(50) &gt; THIS_LEVEL_CHILDREN(40) &gt; THIS_LEVEL(30) &gt; CUSTOMIZE(20) &gt; SELF(10)</code></p>
<hr>
<h2 id="核心-api-真实代码" tabindex="-1">核心 API（真实代码） <a class="header-anchor" href="#核心-api-真实代码" aria-label="Permalink to &quot;核心 API（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="datascope-注解" tabindex="-1">@DataScope 注解 <a class="header-anchor" href="#datascope-注解" aria-label="Permalink to &quot;@DataScope 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScope.java - 标记在 Mapper 类或方法上</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ElementType.TYPE, ElementType.METHOD})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Retention</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RetentionPolicy.RUNTIME)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 是否忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 数据权限字段配置</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {};</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="datacolumn-注解" tabindex="-1">@DataColumn 注解 <a class="header-anchor" href="#datacolumn-注解" aria-label="Permalink to &quot;@DataColumn 注解&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataColumn.java - 定义数据权限字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ElementType.METHOD})</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Retention</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(RetentionPolicy.RUNTIME)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 表别名（用于多表关联查询）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">alias</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ""</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 字段名称（默认 created_by）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    String </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Entity.CREATE_USER_COLUMN;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * Java 类型</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;?></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> javaClass</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long.class;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 权限资源类型（默认 USER）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DataResourceType </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 权限范围（默认跟随系统）</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    DataScopeType </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.IGNORE;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="注解方式使用" tabindex="-1">注解方式使用 <a class="header-anchor" href="#注解方式使用" aria-label="Permalink to &quot;注解方式使用&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="在-mapper-上使用" tabindex="-1">在 Mapper 上使用 <a class="header-anchor" href="#在-mapper-上使用" aria-label="Permalink to &quot;在 Mapper 上使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderMapper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BaseMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 使用默认字段 created_by 进行数据权限过滤</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 指定表别名和字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectListWithDetail</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 多字段数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "org_id"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">resource</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.ORG)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectComplexPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">ignore</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByIdIgnorePermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="编程方式使用-真实代码" tabindex="-1">编程方式使用（真实代码） <a class="header-anchor" href="#编程方式使用-真实代码" aria-label="Permalink to &quot;编程方式使用（真实代码）&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="datapermissionruleholder-api" tabindex="-1">DataPermissionRuleHolder API <a class="header-anchor" href="#datapermissionruleholder-api" aria-label="Permalink to &quot;DataPermissionRuleHolder API&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataPermissionRuleHolder.java - 编程式数据权限控制</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataPermissionRuleHolder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 获取当前的 DataPermissionRule</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">peek</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 入栈一个 DataPermissionRule</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataPermissionRule </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 弹出最顶部 DataPermissionRule</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> poll</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 清除 ThreadLocal</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> clear</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="datapermissionutils-工具类" tabindex="-1">DataPermissionUtils 工具类 <a class="header-anchor" href="#datapermissionutils-工具类" aria-label="Permalink to &quot;DataPermissionUtils 工具类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataPermissionUtils.java</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用默认规则执行（created_by 字段）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orders </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeDefaultDataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 使用指定规则执行</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule.Column.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">alias</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    ))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(rule, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), wrapper);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">DataPermissionRule ignoreRule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Order order </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(ignoreRule, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre>
</div><h3 id="datapermissionrule-规则类" tabindex="-1">DataPermissionRule 规则类 <a class="header-anchor" href="#datapermissionrule-规则类" aria-label="Permalink to &quot;DataPermissionRule 规则类&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataPermissionRule.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataPermissionRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 是否忽略数据权限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ignore;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 规则字段列表</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> columns;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> static</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String alias;                              </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 表别名</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String name </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Entity.CREATE_USER_COLUMN;   </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 字段名（默认 created_by）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Class&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> javaClass </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long.class;           </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Java 类型</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType resource </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataResourceType.USER;  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 资源类型</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.Default</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType scopeType </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.IGNORE;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 权限范围</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="数据权限实现原理" tabindex="-1">数据权限实现原理 <a class="header-anchor" href="#数据权限实现原理" aria-label="Permalink to &quot;数据权限实现原理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="datascopepermissionhandler" tabindex="-1">DataScopePermissionHandler <a class="header-anchor" href="#datascopepermissionhandler" aria-label="Permalink to &quot;DataScopePermissionHandler&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopePermissionHandler.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopePermissionHandler</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MultiDataPermissionHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> AuthenticationContext context;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getSqlSegment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Expression </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">where</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 1. 匿名用户不处理</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">anonymous</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 2. 优先从 DataPermissionRuleHolder 获取规则（编程式）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRuleHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">peek</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 3. 其次从注解获取规则</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionRuleByMappedStatementId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mappedStatementId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 4. 构建 SQL 条件</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(table, rule);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Expression </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildAnnotationExpression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Table </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">table</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, DataPermissionRule </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">rule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isIgnore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dataPermission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 全部权限不过滤</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataScopeType.ALL) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 构建条件</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Expression</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> conditions </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildConditions</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            context, table, rule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getColumns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> conditions.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reduce</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(AndExpression</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::new</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orElse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="数据权限服务实现" tabindex="-1">数据权限服务实现 <a class="header-anchor" href="#数据权限服务实现" aria-label="Permalink to &quot;数据权限服务实现&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 来自 DataScopeServiceImpl.java</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Slf4j</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeServiceImpl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DataScopeService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> RoleMapper roleMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRefMapper dataPermissionRefMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UserMapper userMapper;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrgService orgService;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataScopeById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">orgId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Role</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> list </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">findRoleByUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (CollectionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isEmpty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 找到权限最大的角色（值越大权限越大）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Role role </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> list.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Comparator.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">comparingInt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(item </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> item.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermission permission </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">scopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> CUSTOMIZE) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 自定义：查询角色关联的组织下的用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dataPermissionRefMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(...)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataPermissionRef</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getDataId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 本级：同组织用户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">eq</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgId))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">else</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getScopeType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THIS_LEVEL_CHILDREN) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 本级及子级</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Long</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> orgIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orgService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFullTreeIdPath</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(orgId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">in</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(User</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getOrgId, orgIdList))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stream</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Entity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getId).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (userIdList </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            permission.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getDataPermissionMap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">put</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER, userIdList);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> permission;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="实战示例" tabindex="-1">实战示例 <a class="header-anchor" href="#实战示例" aria-label="Permalink to &quot;实战示例&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="service-层使用" tabindex="-1">Service 层使用 <a class="header-anchor" href="#service-层使用" aria-label="Permalink to &quot;Service 层使用&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> OrderMapper orderMapper;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 方式一：依赖 Mapper 上的 @DataScope 注解</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPageList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), req);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 方式二：编程式控制</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">pageListWithCustomRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        DataPermissionRule rule </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">columns</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(List.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                DataPermissionRule.Column.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">resource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(DataResourceType.USER)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                    .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            ))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            .</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(rule, () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(req.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">buildPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                Wraps.</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lbQ</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">orderByDesc</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Order</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">getCreatedTime));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 方式三：临时忽略数据权限（管理员操作）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Order </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getByIdForAdmin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DataPermissionUtils.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">executeWithRule</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            DataPermissionRule.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ignore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> orderMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="多表关联查询" tabindex="-1">多表关联查询 <a class="header-anchor" href="#多表关联查询" aria-label="Permalink to &quot;多表关联查询&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Mapper</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OrderMapper</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extends</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BaseMapper</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // XML 中的 SQL：SELECT o.*, u.nick_name FROM t_order o LEFT JOIN t_user u ON o.created_by = u.id</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataScope</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">columns</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">DataColumn</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">alias</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "o"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "created_by"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectPageWithUser</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(IPage&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">OrderVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Param</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"req"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) OrderPageReq </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">req</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><hr>
<h2 id="配置说明" tabindex="-1">配置说明 <a class="header-anchor" href="#配置说明" aria-label="Permalink to &quot;配置说明&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    data-permission</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      enabled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">       # 启用数据权限</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      remote</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 是否远程获取权限（对时效性要求高时开启）</span></span></code></pre>
</div><hr>
<h2 id="最佳实践" tabindex="-1">最佳实践 <a class="header-anchor" href="#最佳实践" aria-label="Permalink to &quot;最佳实践&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="_1-优先级" tabindex="-1">1. 优先级 <a class="header-anchor" href="#_1-优先级" aria-label="Permalink to &quot;1. 优先级&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>DataPermissionRuleHolder（编程式） > @DataScope 注解</span></span></code></pre>
</div><p>编程式控制优先级最高，可在运行时动态覆盖注解配置。</p>
<h3 id="_2-字段规范" tabindex="-1">2. 字段规范 <a class="header-anchor" href="#_2-字段规范" aria-label="Permalink to &quot;2. 字段规范&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> t_order</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">bigint</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    -- 业务字段...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    -- 数据权限字段（必须）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    created_by </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">bigint</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'创建人ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    org_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">bigint</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> COMMENT </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'所属组织ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    -- 审计字段</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    created_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">datetime</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    last_modify_time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">datetime</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 添加索引</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> INDEX</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> idx_created_by</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> t_order(created_by);</span></span></code></pre>
</div><h3 id="_3-常见问题" tabindex="-1">3. 常见问题 <a class="header-anchor" href="#_3-常见问题" aria-label="Permalink to &quot;3. 常见问题&quot;">&ZeroWidthSpace;</a></h3>
<p><strong>Q: 多角色如何处理？</strong>
A: 取权限最大的角色（<code>scopeType</code> 值最大）。</p>
<p><strong>Q: 如何临时忽略数据权限？</strong>
A: 使用 <code>@DataScope(ignore = true)</code> 或 <code>DataPermissionUtils.executeWithRule(ignoreRule, ...)</code>。</p>
<hr>
<h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/advanced/">进阶开发</a> - 学习进阶开发技巧</li>
<li><a href="/zh/guide/advanced/performance.html">性能优化</a> - 了解性能优化方案</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[租户管理详解 ]]></title>
            <link>https://docs.battcn.com/zh/guide/saas/tenant.html</link>
            <guid>https://docs.battcn.com/zh/guide/saas/tenant.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="租户管理详解" tabindex="-1">租户管理详解 <a class="header-anchor" href="#租户管理详解" aria-label="Permalink to &quot;租户管理详解&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 租户管理的完整流程和配置方法</p>
</div>
<h2 id="租户生命周期" tabindex="-1">租户生命周期 <a class="header-anchor" href="#租户生命周期" aria-label="Permalink to &quot;租户生命周期&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>创建租户 → 初始化数据 → 配置功能 → 正常运营 → 续费/升级 → 停用/删除</span></span></code></pre>
</div><h2 id="租户创建" tabindex="-1">租户创建 <a class="header-anchor" href="#租户创建" aria-label="Permalink to &quot;租户创建&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="创建流程" tabindex="-1">创建流程 <a class="header-anchor" href="#创建流程" aria-label="Permalink to &quot;创建流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>┌─────────────┐     ┌─────────────┐           ▼</span></span>
<span class="line"><span>│  创建完成   │ ◀── │  创建管理员  │ ◀── ┌─────────────┐</span></span>
<span class="line"><span>└─────────────┘     └─────────────┘     │  分配套餐   │</span></span>
<span class="line"><span>                                        └─────────────┘</span></span></code></pre>
</div><h3 id="基础信息" tabindex="-1">基础信息 <a class="header-anchor" href="#基础信息" aria-label="Permalink to &quot;基础信息&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>租户编码</td>
<td>唯一标识，不可修改</td>
<td>T202401001</td>
</tr>
<tr>
<td>租户名称</td>
<td>企业/组织名称</td>
<td>北京科技有限公司</td>
</tr>
<tr>
<td>联系人</td>
<td>租户负责人</td>
<td>张三</td>
</tr>
<tr>
<td>联系电话</td>
<td>联系方式</td>
<td>13800138000</td>
</tr>
<tr>
<td>有效期</td>
<td>租户使用期限</td>
<td>2024-01-01 ~ 2024-12-31</td>
</tr>
<tr>
<td>状态</td>
<td>租户状态</td>
<td>正常/禁用/过期</td>
</tr>
</tbody>
</table>
<h3 id="后台配置" tabindex="-1">后台配置 <a class="header-anchor" href="#后台配置" aria-label="Permalink to &quot;后台配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 创建租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    tenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// TenantServiceImpl</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 1. 保存租户基础信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, Tenant.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantStatus.NORMAL);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 2. 创建租户数据源（数据源隔离模式）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenantType </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantType.DATASOURCE) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        createTenantDatabase</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 3. 初始化租户数据</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    initTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 4. 创建租户管理员</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    createTenantAdmin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 5. 分配产品套餐</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        assignProduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="数据源配置" tabindex="-1">数据源配置 <a class="header-anchor" href="#数据源配置" aria-label="Permalink to &quot;数据源配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="字段隔离配置" tabindex="-1">字段隔离配置 <a class="header-anchor" href="#字段隔离配置" aria-label="Permalink to &quot;字段隔离配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">column</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                    # 隔离类型：字段隔离</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      tenant-column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">tenant_id</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 租户字段名</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      include-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 需要租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_order</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_customer</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_product</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      ignore-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 忽略租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_tenant</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_dict</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_config</span></span></code></pre>
</div><h3 id="数据源隔离配置" tabindex="-1">数据源隔离配置 <a class="header-anchor" href="#数据源隔离配置" aria-label="Permalink to &quot;数据源隔离配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">datasource</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">              # 隔离类型：数据源隔离</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">feign</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">               # 数据源获取策略（非IAM模块使用feign）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      db-notify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">              # 数据源变更通知方式</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 主数据源配置</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  datasource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    dynamic</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      primary</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">master</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      datasource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          driver-class-name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">com.mysql.cj.jdbc.Driver</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">jdbc:mysql://localhost:3306/v4-dev_master?...</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">root</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          password</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span></code></pre>
</div><h3 id="动态数据源初始化" tabindex="-1">动态数据源初始化 <a class="header-anchor" href="#动态数据源初始化" aria-label="Permalink to &quot;动态数据源初始化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 启动时加载所有租户数据源</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantDataSourceInitializer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CommandLineRunner</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantService tenantService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DynamicRoutingDataSource dataSource;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String... </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取所有租户数据源配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantDataSource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> tenantDataSources </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAllDataSources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 添加到动态数据源</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (TenantDataSource tds </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantDataSources) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            DataSourceProperty property </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> buildDataSourceProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tds);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            dataSource.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addDataSource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tds.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                dataSourceCreator.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createDataSource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(property));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="数据初始化" tabindex="-1">数据初始化 <a class="header-anchor" href="#数据初始化" aria-label="Permalink to &quot;数据初始化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="初始化脚本" tabindex="-1">初始化脚本 <a class="header-anchor" href="#初始化脚本" aria-label="Permalink to &quot;初始化脚本&quot;">&ZeroWidthSpace;</a></h3>
<p>新租户创建时执行的初始化 SQL 位于：<code>附件/mysql/tenant_schema.sql</code></p>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 基础角色</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_role (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, code, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">description</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">VALUES</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'管理员'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ADMIN'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户管理员，拥有全部权限'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'普通用户'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'USER'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'普通用户，基础权限'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 基础菜单权限（从主库复制）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_menu (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_menu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 角色菜单关联</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_role_menu (role_id, menu_id)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_role r, sys_menu m </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">code</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ADMIN'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 系统字典</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_dict (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_dict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_dict_item (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_dict_item</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 系统配置</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_config (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h3 id="初始化代码" tabindex="-1">初始化代码 <a class="header-anchor" href="#初始化代码" aria-label="Permalink to &quot;初始化代码&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantInitService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 初始化租户数据</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 切换到租户数据源</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化角色</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initRoles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化菜单</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initMenus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化字典</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initDicts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initConfigs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clear</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initRoles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Role</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Arrays.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">asList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"管理员"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ADMIN"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"普通用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"USER"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insertBatch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(roles);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="产品套餐" tabindex="-1">产品套餐 <a class="header-anchor" href="#产品套餐" aria-label="Permalink to &quot;产品套餐&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="套餐配置" tabindex="-1">套餐配置 <a class="header-anchor" href="#套餐配置" aria-label="Permalink to &quot;套餐配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Product</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String name;          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 套餐名称</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String code;          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 套餐编码</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer maxUsers;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 最大用户数</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long maxStorage;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 最大存储空间（字节）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String features;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 功能列表（JSON）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BigDecimal price;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 价格</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer period;       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 有效期（天）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="套餐功能控制" tabindex="-1">套餐功能控制 <a class="header-anchor" href="#套餐功能控制" aria-label="Permalink to &quot;套餐功能控制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FeatureService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 检查功能是否可用</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> checkFeature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">featureCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        TenantProduct tp </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantProductMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (tp </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getExpireTime</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isBefore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LocalDateTime.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">now</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> features </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFeatures</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), String.class);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> features.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(featureCode);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 检查用户数是否超限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> checkUserLimit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        TenantProduct tp </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantProductMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        int</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> currentUsers </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">countByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (currentUsers </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMaxUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户数已达上限，请升级套餐"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="租户切换" tabindex="-1">租户切换 <a class="header-anchor" href="#租户切换" aria-label="Permalink to &quot;租户切换&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="用户多租户" tabindex="-1">用户多租户 <a class="header-anchor" href="#用户多租户" aria-label="Permalink to &quot;用户多租户&quot;">&ZeroWidthSpace;</a></h3>
<p>一个用户可以属于多个租户，支持在租户间切换：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 获取用户所属租户列表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserTenants</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> tenants </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userTenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantsByUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenants);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants/{tenantId}/switch"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoginVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> switchTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 验证用户是否属于该租户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> belongs </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userTenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">checkUserBelongs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId, tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">belongs) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"您不属于该租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 生成新的 Token</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    LoginVO loginVO </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> authService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">switchTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId, tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(loginVO);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="租户状态管理" tabindex="-1">租户状态管理 <a class="header-anchor" href="#租户状态管理" aria-label="Permalink to &quot;租户状态管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="状态说明" tabindex="-1">状态说明 <a class="header-anchor" href="#状态说明" aria-label="Permalink to &quot;状态说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>状态</th>
<th>说明</th>
<th>可用性</th>
</tr>
</thead>
<tbody>
<tr>
<td>NORMAL</td>
<td>正常</td>
<td>可正常访问</td>
</tr>
<tr>
<td>DISABLED</td>
<td>禁用</td>
<td>不可登录，数据保留</td>
</tr>
<tr>
<td>EXPIRED</td>
<td>过期</td>
<td>提示续费，限制功能</td>
</tr>
<tr>
<td>DELETED</td>
<td>删除</td>
<td>数据已清除</td>
</tr>
</tbody>
</table>
<h3 id="状态流转" tabindex="-1">状态流转 <a class="header-anchor" href="#状态流转" aria-label="Permalink to &quot;状态流转&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>NORMAL ──▶ DISABLED ──▶ DELETED</span></span>
<span class="line"><span>   │            │</span></span>
<span class="line"><span>   ▼            ▼</span></span>
<span class="line"><span>EXPIRED ──▶ NORMAL（续费后）</span></span></code></pre>
</div><h3 id="过期处理" tabindex="-1">过期处理 <a class="header-anchor" href="#过期处理" aria-label="Permalink to &quot;过期处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Scheduled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cron</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "0 0 1 * * ?"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 每天凌晨1点执行</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> checkTenantExpiry</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查找即将过期的租户（7天内）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> expiring </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectExpiringSoon</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> expiring) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送续费提醒</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendExpiryReminder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 处理已过期租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> expired </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectExpired</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> expired) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantStatus.EXPIRED);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送过期通知</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendExpiredNotice</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="前端租户管理" tabindex="-1">前端租户管理 <a class="header-anchor" href="#前端租户管理" aria-label="Permalink to &quot;前端租户管理&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useFs } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getTenantList, createTenant, updateTenant, deleteTenant } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useFs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getTenantList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.id, form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      code: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户编码'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        addForm: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        editForm: { disabled: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      name: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      contact: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'联系人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      phone: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'联系电话'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      expireTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'到期时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dict: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'正常'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'NORMAL'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'DISABLED'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'warning'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'过期'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'EXPIRED'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">crudBinding</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/saas/permission.html">数据权限</a> - 了解数据权限控制</li>
<li><a href="/zh/guide/packages/tenant.html">租户配置</a> - 技术层面的租户配置</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="租户管理详解" tabindex="-1">租户管理详解 <a class="header-anchor" href="#租户管理详解" aria-label="Permalink to &quot;租户管理详解&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">学习目标</p>
<p>掌握 Wemirr Platform 租户管理的完整流程和配置方法</p>
</div>
<h2 id="租户生命周期" tabindex="-1">租户生命周期 <a class="header-anchor" href="#租户生命周期" aria-label="Permalink to &quot;租户生命周期&quot;">&ZeroWidthSpace;</a></h2>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>创建租户 → 初始化数据 → 配置功能 → 正常运营 → 续费/升级 → 停用/删除</span></span></code></pre>
</div><h2 id="租户创建" tabindex="-1">租户创建 <a class="header-anchor" href="#租户创建" aria-label="Permalink to &quot;租户创建&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="创建流程" tabindex="-1">创建流程 <a class="header-anchor" href="#创建流程" aria-label="Permalink to &quot;创建流程&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><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>┌─────────────┐     ┌─────────────┐           ▼</span></span>
<span class="line"><span>│  创建完成   │ ◀── │  创建管理员  │ ◀── ┌─────────────┐</span></span>
<span class="line"><span>└─────────────┘     └─────────────┘     │  分配套餐   │</span></span>
<span class="line"><span>                                        └─────────────┘</span></span></code></pre>
</div><h3 id="基础信息" tabindex="-1">基础信息 <a class="header-anchor" href="#基础信息" aria-label="Permalink to &quot;基础信息&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>字段</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>租户编码</td>
<td>唯一标识，不可修改</td>
<td>T202401001</td>
</tr>
<tr>
<td>租户名称</td>
<td>企业/组织名称</td>
<td>北京科技有限公司</td>
</tr>
<tr>
<td>联系人</td>
<td>租户负责人</td>
<td>张三</td>
</tr>
<tr>
<td>联系电话</td>
<td>联系方式</td>
<td>13800138000</td>
</tr>
<tr>
<td>有效期</td>
<td>租户使用期限</td>
<td>2024-01-01 ~ 2024-12-31</td>
</tr>
<tr>
<td>状态</td>
<td>租户状态</td>
<td>正常/禁用/过期</td>
</tr>
</tbody>
</table>
<h3 id="后台配置" tabindex="-1">后台配置 <a class="header-anchor" href="#后台配置" aria-label="Permalink to &quot;后台配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 创建租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SysLog</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"创建租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Void</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequestBody</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Valid</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    tenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// TenantServiceImpl</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Transactional</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">rollbackFor</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Exception.class)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> create</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantDTO dto) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 1. 保存租户基础信息</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BeanUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">copyProperties</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dto, Tenant.class);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantStatus.NORMAL);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insert</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 2. 创建租户数据源（数据源隔离模式）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (multiTenantType </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantType.DATASOURCE) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        createTenantDatabase</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 3. 初始化租户数据</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    initTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 4. 创建租户管理员</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    createTenantAdmin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 5. 分配产品套餐</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        assignProduct</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), dto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getProductId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="数据源配置" tabindex="-1">数据源配置 <a class="header-anchor" href="#数据源配置" aria-label="Permalink to &quot;数据源配置&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="字段隔离配置" tabindex="-1">字段隔离配置 <a class="header-anchor" href="#字段隔离配置" aria-label="Permalink to &quot;字段隔离配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">column</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">                    # 隔离类型：字段隔离</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      tenant-column</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">tenant_id</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        # 租户字段名</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      include-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                 </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 需要租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_order</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_customer</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">t_product</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      ignore-tables</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:                  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 忽略租户过滤的表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_tenant</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_dict</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        - </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">sys_config</span></span></code></pre>
</div><h3 id="数据源隔离配置" tabindex="-1">数据源隔离配置 <a class="header-anchor" href="#数据源隔离配置" aria-label="Permalink to &quot;数据源隔离配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-yaml vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">yaml</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># application.yml</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">extend</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  mybatis-plus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    multi-tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">datasource</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">              # 隔离类型：数据源隔离</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strategy</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">feign</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">               # 数据源获取策略（非IAM模块使用feign）</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      db-notify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">redis</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">              # 数据源变更通知方式</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># 主数据源配置</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">spring</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">  datasource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">    dynamic</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      primary</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">master</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      strict</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">      datasource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">        master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          driver-class-name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">com.mysql.cj.jdbc.Driver</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          url</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">jdbc:mysql://localhost:3306/v4-dev_master?...</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          username</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">root</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">          password</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">123456</span></span></code></pre>
</div><h3 id="动态数据源初始化" tabindex="-1">动态数据源初始化 <a class="header-anchor" href="#动态数据源初始化" aria-label="Permalink to &quot;动态数据源初始化&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 启动时加载所有租户数据源</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Component</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">RequiredArgsConstructor</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantDataSourceInitializer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> implements</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CommandLineRunner</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TenantService tenantService;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> final</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> DynamicRoutingDataSource dataSource;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    @</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Override</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String... </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">args</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 获取所有租户数据源配置</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantDataSource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> tenantDataSources </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAllDataSources</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 添加到动态数据源</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (TenantDataSource tds </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantDataSources) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            DataSourceProperty property </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> buildDataSourceProperty</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tds);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            dataSource.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addDataSource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tds.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">                dataSourceCreator.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createDataSource</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(property));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="数据初始化" tabindex="-1">数据初始化 <a class="header-anchor" href="#数据初始化" aria-label="Permalink to &quot;数据初始化&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="初始化脚本" tabindex="-1">初始化脚本 <a class="header-anchor" href="#初始化脚本" aria-label="Permalink to &quot;初始化脚本&quot;">&ZeroWidthSpace;</a></h3>
<p>新租户创建时执行的初始化 SQL 位于：<code>附件/mysql/tenant_schema.sql</code></p>
<div class="language-sql vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">sql</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 基础角色</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_role (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, code, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">description</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">VALUES</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'管理员'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ADMIN'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户管理员，拥有全部权限'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'普通用户'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'USER'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'普通用户，基础权限'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 基础菜单权限（从主库复制）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_menu (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_menu</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 角色菜单关联</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_role_menu (role_id, menu_id)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">m</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_role r, sys_menu m </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">WHERE</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> r</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">code</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'ADMIN'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 系统字典</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_dict (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_dict</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_dict_item (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_dict_item</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">-- 系统配置</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">INSERT INTO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sys_config (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">SELECT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> FROM</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">sys_config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> WHERE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenant_id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h3 id="初始化代码" tabindex="-1">初始化代码 <a class="header-anchor" href="#初始化代码" aria-label="Permalink to &quot;初始化代码&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TenantInitService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 初始化租户数据</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initTenantData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 切换到租户数据源</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        try</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化角色</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initRoles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化菜单</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initMenus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化字典</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initDicts</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // 初始化配置</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            initConfigs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">finally</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            TenantContextHolder.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clear</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initRoles</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Long </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Role</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> roles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Arrays.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">asList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"管理员"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ADMIN"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            Role.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"普通用户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">code</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"USER"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        roleMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">insertBatch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(roles);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="产品套餐" tabindex="-1">产品套餐 <a class="header-anchor" href="#产品套餐" aria-label="Permalink to &quot;产品套餐&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="套餐配置" tabindex="-1">套餐配置 <a class="header-anchor" href="#套餐配置" aria-label="Permalink to &quot;套餐配置&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Product</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long id;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String name;          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 套餐名称</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String code;          </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 套餐编码</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer maxUsers;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 最大用户数</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long maxStorage;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 最大存储空间（字节）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> String features;      </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 功能列表（JSON）</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> BigDecimal price;     </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 价格</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    private</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Integer period;       </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 有效期（天）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="套餐功能控制" tabindex="-1">套餐功能控制 <a class="header-anchor" href="#套餐功能控制" aria-label="Permalink to &quot;套餐功能控制&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Service</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> class</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FeatureService</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 检查功能是否可用</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> boolean</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> checkFeature</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(String </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">featureCode</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        TenantProduct tp </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantProductMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (tp </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getExpireTime</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">isBefore</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(LocalDateTime.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">now</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">())) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">String</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> features </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> JsonUtil.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getFeatures</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), String.class);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> features.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">contains</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(featureCode);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    /**</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     * 检查用户数是否超限</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">     */</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> checkUserLimit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        Long tenantId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        TenantProduct tp </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantProductMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        int</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> currentUsers </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">countByTenantId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (currentUsers </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tp.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getMaxUsers</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">            throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"用户数已达上限，请升级套餐"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="租户切换" tabindex="-1">租户切换 <a class="header-anchor" href="#租户切换" aria-label="Permalink to &quot;租户切换&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="用户多租户" tabindex="-1">用户多租户 <a class="header-anchor" href="#用户多租户" aria-label="Permalink to &quot;用户多租户&quot;">&ZeroWidthSpace;</a></h3>
<p>一个用户可以属于多个租户，支持在租户间切换：</p>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 获取用户所属租户列表</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">GetMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">List</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">TenantVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getUserTenants</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TenantVO</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> tenants </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userTenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTenantsByUserId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenants);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 切换租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PostMapping</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/tenants/{tenantId}/switch"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">LoginVO</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> switchTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">PathVariable</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Long tenantId) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Long userId </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">userId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 验证用户是否属于该租户</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> belongs </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> userTenantService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">checkUserBelongs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId, tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">belongs) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> BizException</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"您不属于该租户"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 生成新的 Token</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    LoginVO loginVO </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> authService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">switchTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(userId, tenantId);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Result.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(loginVO);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="租户状态管理" tabindex="-1">租户状态管理 <a class="header-anchor" href="#租户状态管理" aria-label="Permalink to &quot;租户状态管理&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="状态说明" tabindex="-1">状态说明 <a class="header-anchor" href="#状态说明" aria-label="Permalink to &quot;状态说明&quot;">&ZeroWidthSpace;</a></h3>
<table tabindex="0">
<thead>
<tr>
<th>状态</th>
<th>说明</th>
<th>可用性</th>
</tr>
</thead>
<tbody>
<tr>
<td>NORMAL</td>
<td>正常</td>
<td>可正常访问</td>
</tr>
<tr>
<td>DISABLED</td>
<td>禁用</td>
<td>不可登录，数据保留</td>
</tr>
<tr>
<td>EXPIRED</td>
<td>过期</td>
<td>提示续费，限制功能</td>
</tr>
<tr>
<td>DELETED</td>
<td>删除</td>
<td>数据已清除</td>
</tr>
</tbody>
</table>
<h3 id="状态流转" tabindex="-1">状态流转 <a class="header-anchor" href="#状态流转" aria-label="Permalink to &quot;状态流转&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>NORMAL ──▶ DISABLED ──▶ DELETED</span></span>
<span class="line"><span>   │            │</span></span>
<span class="line"><span>   ▼            ▼</span></span>
<span class="line"><span>EXPIRED ──▶ NORMAL（续费后）</span></span></code></pre>
</div><h3 id="过期处理" tabindex="-1">过期处理 <a class="header-anchor" href="#过期处理" aria-label="Permalink to &quot;过期处理&quot;">&ZeroWidthSpace;</a></h3>
<div class="language-java vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">@</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Scheduled</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cron</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "0 0 1 * * ?"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)  </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// 每天凌晨1点执行</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">public</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> void</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> checkTenantExpiry</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 查找即将过期的租户（7天内）</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> expiring </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectExpiringSoon</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> expiring) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送续费提醒</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendExpiryReminder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // 处理已过期租户</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    List&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">Tenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> expired </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">selectExpired</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (Tenant tenant </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> expired) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        tenant.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setStatus</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(TenantStatus.EXPIRED);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        tenantMapper.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">updateById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">        // 发送过期通知</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        notifyService.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sendExpiredNotice</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(tenant);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h2 id="前端租户管理" tabindex="-1">前端租户管理 <a class="header-anchor" href="#前端租户管理" aria-label="Permalink to &quot;前端租户管理&quot;">&ZeroWidthSpace;</a></h2>
<div class="language-vue vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> setup</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { useFs } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@fast-crud/fast-crud'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { getTenantList, createTenant, updateTenant, deleteTenant } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './api'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudBinding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">crudRef</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useFs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  crudOptions: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    request: {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pageRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">query</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getTenantList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      addRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      editRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> updateTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.id, form),</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      delRequest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">row</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> deleteTenant</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(row.id),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    columns: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      code: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户编码'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        addForm: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        editForm: { disabled: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      name: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'租户名称'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        search: { show: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      contact: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'联系人'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      phone: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'联系电话'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      expireTime: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'到期时间'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'datetime'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      status: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        title: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'状态'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'dict-select'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        dict: {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          data: [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'正常'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'NORMAL'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'success'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'禁用'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'DISABLED'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'warning'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">            { label: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'过期'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, value: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'EXPIRED'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, color: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'error'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ],</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">fs-crud</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ref</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"crudRef"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> v-bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">crudBinding</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">template</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre>
</div><h2 id="下一步" tabindex="-1">下一步 <a class="header-anchor" href="#下一步" aria-label="Permalink to &quot;下一步&quot;">&ZeroWidthSpace;</a></h2>
<div class="next-steps">
<ul>
<li><a href="/zh/guide/saas/permission.html">数据权限</a> - 了解数据权限控制</li>
<li><a href="/zh/guide/packages/tenant.html">租户配置</a> - 技术层面的租户配置</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[授权登记 ]]></title>
            <link>https://docs.battcn.com/zh/market/authorized.html</link>
            <guid>https://docs.battcn.com/zh/market/authorized.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="授权登记" tabindex="-1">授权登记 <a class="header-anchor" href="#授权登记" aria-label="Permalink to &quot;授权登记&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">说明</p>
<p>任何自用修改 Wemirr Platform 源码的企业或个人，需在此登记备案，登记后视为合规使用。</p>
<ul>
<li><strong>企业用户</strong>：提供技术支持</li>
<li><strong>个人学习</strong>：不提供技术支持</li>
</ul>
</div>
<hr>
<h2 id="登记方式" tabindex="-1">登记方式 <a class="header-anchor" href="#登记方式" aria-label="Permalink to &quot;登记方式&quot;">&ZeroWidthSpace;</a></h2>
<p>前往 Gitee Issue 评论区留言登记：</p>
<p>👉 <a href="https://gitee.com/battcn/wemirr-platform/issues/I85RJS" target="_blank" rel="noreferrer">点击前往登记</a></p>
<h3 id="登记格式" tabindex="-1">登记格式 <a class="header-anchor" href="#登记格式" aria-label="Permalink to &quot;登记格式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>| 序号 | 登记人 | 登记时间 | 公司/组织 | 用途说明 |</span></span></code></pre>
</div><hr>
<h2 id="已登记企业" tabindex="-1">已登记企业 <a class="header-anchor" href="#已登记企业" aria-label="Permalink to &quot;已登记企业&quot;">&ZeroWidthSpace;</a></h2>
<NavCard :items=data /><hr>
<h2 id="授权说明" tabindex="-1">授权说明 <a class="header-anchor" href="#授权说明" aria-label="Permalink to &quot;授权说明&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="允许" tabindex="-1">允许 <a class="header-anchor" href="#允许" aria-label="Permalink to &quot;允许&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>✅ 企业内部使用</li>
<li>✅ 基于源码二次开发</li>
<li>✅ 学习研究用途</li>
</ul>
<h3 id="禁止" tabindex="-1">禁止 <a class="header-anchor" href="#禁止" aria-label="Permalink to &quot;禁止&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>❌ 未登记商业使用</li>
<li>❌ 删除版权信息后使用</li>
<li>❌ 以任何形式售卖源码</li>
</ul>
]]></description>
            <content:encoded><![CDATA[<h1 id="授权登记" tabindex="-1">授权登记 <a class="header-anchor" href="#授权登记" aria-label="Permalink to &quot;授权登记&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">说明</p>
<p>任何自用修改 Wemirr Platform 源码的企业或个人，需在此登记备案，登记后视为合规使用。</p>
<ul>
<li><strong>企业用户</strong>：提供技术支持</li>
<li><strong>个人学习</strong>：不提供技术支持</li>
</ul>
</div>
<hr>
<h2 id="登记方式" tabindex="-1">登记方式 <a class="header-anchor" href="#登记方式" aria-label="Permalink to &quot;登记方式&quot;">&ZeroWidthSpace;</a></h2>
<p>前往 Gitee Issue 评论区留言登记：</p>
<p>👉 <a href="https://gitee.com/battcn/wemirr-platform/issues/I85RJS" target="_blank" rel="noreferrer">点击前往登记</a></p>
<h3 id="登记格式" tabindex="-1">登记格式 <a class="header-anchor" href="#登记格式" aria-label="Permalink to &quot;登记格式&quot;">&ZeroWidthSpace;</a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>| 序号 | 登记人 | 登记时间 | 公司/组织 | 用途说明 |</span></span></code></pre>
</div><hr>
<h2 id="已登记企业" tabindex="-1">已登记企业 <a class="header-anchor" href="#已登记企业" aria-label="Permalink to &quot;已登记企业&quot;">&ZeroWidthSpace;</a></h2>
<NavCard :items=data /><hr>
<h2 id="授权说明" tabindex="-1">授权说明 <a class="header-anchor" href="#授权说明" aria-label="Permalink to &quot;授权说明&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="允许" tabindex="-1">允许 <a class="header-anchor" href="#允许" aria-label="Permalink to &quot;允许&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>✅ 企业内部使用</li>
<li>✅ 基于源码二次开发</li>
<li>✅ 学习研究用途</li>
</ul>
<h3 id="禁止" tabindex="-1">禁止 <a class="header-anchor" href="#禁止" aria-label="Permalink to &quot;禁止&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>❌ 未登记商业使用</li>
<li>❌ 删除版权信息后使用</li>
<li>❌ 以任何形式售卖源码</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[技术服务 ]]></title>
            <link>https://docs.battcn.com/zh/market/contributors.html</link>
            <guid>https://docs.battcn.com/zh/market/contributors.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="技术服务" tabindex="-1">技术服务 <a class="header-anchor" href="#技术服务" aria-label="Permalink to &quot;技术服务&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">专业团队</p>
<p>我们是一支专注于企业级应用开发的技术团队，核心成员来自 <strong>阿里巴巴</strong>、<strong>菜鸟网络</strong>、<strong>掌门教育</strong>、<strong>大华股份</strong> 等知名企业，平均 <strong>10+ 年</strong>开发经验。</p>
</div>
<hr>
<h2 id="团队优势" tabindex="-1">团队优势 <a class="header-anchor" href="#团队优势" aria-label="Permalink to &quot;团队优势&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>优势</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>技术实力</strong></td>
<td>精通 Java、Spring Cloud、Vue3 等主流技术栈</td>
</tr>
<tr>
<td><strong>行业经验</strong></td>
<td>深耕物流、电商、教育、制造等行业多年</td>
</tr>
<tr>
<td><strong>交付质量</strong></td>
<td>代码规范、文档齐全、测试覆盖</td>
</tr>
<tr>
<td><strong>响应速度</strong></td>
<td>7×12 小时在线支持，问题快速响应</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="服务内容" tabindex="-1">服务内容 <a class="header-anchor" href="#服务内容" aria-label="Permalink to &quot;服务内容&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="🔧-定制开发" tabindex="-1">🔧 定制开发 <a class="header-anchor" href="#🔧-定制开发" aria-label="Permalink to &quot;🔧 定制开发&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>基于 Wemirr Platform 的二次开发</li>
<li>新业务模块开发</li>
<li>系统功能扩展</li>
<li>第三方系统对接</li>
</ul>
<h3 id="🚀-技术支持" tabindex="-1">🚀 技术支持 <a class="header-anchor" href="#🚀-技术支持" aria-label="Permalink to &quot;🚀 技术支持&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>项目部署上线指导</li>
<li>性能优化调优</li>
<li>疑难问题排查</li>
<li>代码审查建议</li>
</ul>
<h3 id="📚-培训服务" tabindex="-1">📚 培训服务 <a class="header-anchor" href="#📚-培训服务" aria-label="Permalink to &quot;📚 培训服务&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>团队技术培训</li>
<li>项目交接培训</li>
<li>最佳实践分享</li>
</ul>
<hr>
<h2 id="成功案例" tabindex="-1">成功案例 <a class="header-anchor" href="#成功案例" aria-label="Permalink to &quot;成功案例&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>客户</th>
<th>项目</th>
<th>周期</th>
</tr>
</thead>
<tbody>
<tr>
<td>某物流企业</td>
<td>TMS 运输管理系统定制</td>
<td>3个月</td>
</tr>
<tr>
<td>某制造企业</td>
<td>WMS 仓储管理系统定制</td>
<td>2个月</td>
</tr>
<tr>
<td>某电商平台</td>
<td>多租户 SaaS 改造</td>
<td>1个月</td>
</tr>
<tr>
<td>某教育机构</td>
<td>审批流程系统开发</td>
<td>1个月</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="联系我们" tabindex="-1">联系我们 <a class="header-anchor" href="#联系我们" aria-label="Permalink to &quot;联系我们&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">合作咨询</p>
<p>如有定制开发、技术外包、技术咨询等需求，欢迎联系：</p>
<ul>
<li><strong>Gitee</strong>：<a href="https://gitee.com/battcn" target="_blank" rel="noreferrer">https://gitee.com/battcn</a></li>
<li><strong>微信</strong>：添加时请备注「技术合作」</li>
</ul>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="技术服务" tabindex="-1">技术服务 <a class="header-anchor" href="#技术服务" aria-label="Permalink to &quot;技术服务&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">专业团队</p>
<p>我们是一支专注于企业级应用开发的技术团队，核心成员来自 <strong>阿里巴巴</strong>、<strong>菜鸟网络</strong>、<strong>掌门教育</strong>、<strong>大华股份</strong> 等知名企业，平均 <strong>10+ 年</strong>开发经验。</p>
</div>
<hr>
<h2 id="团队优势" tabindex="-1">团队优势 <a class="header-anchor" href="#团队优势" aria-label="Permalink to &quot;团队优势&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>优势</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>技术实力</strong></td>
<td>精通 Java、Spring Cloud、Vue3 等主流技术栈</td>
</tr>
<tr>
<td><strong>行业经验</strong></td>
<td>深耕物流、电商、教育、制造等行业多年</td>
</tr>
<tr>
<td><strong>交付质量</strong></td>
<td>代码规范、文档齐全、测试覆盖</td>
</tr>
<tr>
<td><strong>响应速度</strong></td>
<td>7×12 小时在线支持，问题快速响应</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="服务内容" tabindex="-1">服务内容 <a class="header-anchor" href="#服务内容" aria-label="Permalink to &quot;服务内容&quot;">&ZeroWidthSpace;</a></h2>
<h3 id="🔧-定制开发" tabindex="-1">🔧 定制开发 <a class="header-anchor" href="#🔧-定制开发" aria-label="Permalink to &quot;🔧 定制开发&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>基于 Wemirr Platform 的二次开发</li>
<li>新业务模块开发</li>
<li>系统功能扩展</li>
<li>第三方系统对接</li>
</ul>
<h3 id="🚀-技术支持" tabindex="-1">🚀 技术支持 <a class="header-anchor" href="#🚀-技术支持" aria-label="Permalink to &quot;🚀 技术支持&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>项目部署上线指导</li>
<li>性能优化调优</li>
<li>疑难问题排查</li>
<li>代码审查建议</li>
</ul>
<h3 id="📚-培训服务" tabindex="-1">📚 培训服务 <a class="header-anchor" href="#📚-培训服务" aria-label="Permalink to &quot;📚 培训服务&quot;">&ZeroWidthSpace;</a></h3>
<ul>
<li>团队技术培训</li>
<li>项目交接培训</li>
<li>最佳实践分享</li>
</ul>
<hr>
<h2 id="成功案例" tabindex="-1">成功案例 <a class="header-anchor" href="#成功案例" aria-label="Permalink to &quot;成功案例&quot;">&ZeroWidthSpace;</a></h2>
<table tabindex="0">
<thead>
<tr>
<th>客户</th>
<th>项目</th>
<th>周期</th>
</tr>
</thead>
<tbody>
<tr>
<td>某物流企业</td>
<td>TMS 运输管理系统定制</td>
<td>3个月</td>
</tr>
<tr>
<td>某制造企业</td>
<td>WMS 仓储管理系统定制</td>
<td>2个月</td>
</tr>
<tr>
<td>某电商平台</td>
<td>多租户 SaaS 改造</td>
<td>1个月</td>
</tr>
<tr>
<td>某教育机构</td>
<td>审批流程系统开发</td>
<td>1个月</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="联系我们" tabindex="-1">联系我们 <a class="header-anchor" href="#联系我们" aria-label="Permalink to &quot;联系我们&quot;">&ZeroWidthSpace;</a></h2>
<div class="warning custom-block"><p class="custom-block-title">合作咨询</p>
<p>如有定制开发、技术外包、技术咨询等需求，欢迎联系：</p>
<ul>
<li><strong>Gitee</strong>：<a href="https://gitee.com/battcn" target="_blank" rel="noreferrer">https://gitee.com/battcn</a></li>
<li><strong>微信</strong>：添加时请备注「技术合作」</li>
</ul>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[生态服务 ]]></title>
            <link>https://docs.battcn.com/zh/market/</link>
            <guid>https://docs.battcn.com/zh/market/</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="生态服务" tabindex="-1">生态服务 <a class="header-anchor" href="#生态服务" aria-label="Permalink to &quot;生态服务&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>打造最强SAAS微服务框架</p>
</div>
]]></description>
            <content:encoded><![CDATA[<h1 id="生态服务" tabindex="-1">生态服务 <a class="header-anchor" href="#生态服务" aria-label="Permalink to &quot;生态服务&quot;">&ZeroWidthSpace;</a></h1>
<div class="tip custom-block"><p class="custom-block-title">TIP</p>
<p>打造最强SAAS微服务框架</p>
</div>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[无题]]></title>
            <link>https://docs.battcn.com/zh/market/video.html</link>
            <guid>https://docs.battcn.com/zh/market/video.html</guid>
            <pubDate>Thu, 01 Jul 2021 00:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>