<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>AYO-NET</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <icon>https://zizai.cc/assets/favicon.svg</icon>
  <id>https://zizai.cc/</id>
  <link href="https://zizai.cc/" rel="alternate"/>
  <link href="https://zizai.cc/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, AYO-NET</rights>
  <subtitle>Markdown 驱动的技术与项目文章站</subtitle>
  <title>自在知识库</title>
  <updated>2026-06-13T03:20:00.000Z</updated>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <content>
      <![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p><strong>命名空间</strong>：<code>ZhonTai.Admin.Core</code></p><p>中台 Admin 的启动入口是 <code>HostApp</code> 类，通过 <code>HostAppOptions</code> 提供 <strong>15+ 个扩展回调点</strong>，允许在各阶段注入自定义逻辑。</p><h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>自定义中间件注册</li><li>自定义数据库配置</li><li>自定义服务注册</li><li>自定义 FreeSql 配置</li></ul><hr><h2 id="HostApp-启动流程"><a href="#HostApp-启动流程" class="headerlink" title="HostApp 启动流程"></a>HostApp 启动流程</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">HostApp.Run(args, assembly)</span><br><span class="line">    │</span><br><span class="line">    ├── 1. ConfigurePreWebApplicationBuilder    // 前置 WebApplicationBuilder 配置</span><br><span class="line">    ├── 2. ConfigureWebApplicationBuilder        // WebApplicationBuilder 配置</span><br><span class="line">    ├── 3. ConfigurePreServices                  // 前置服务注册</span><br><span class="line">    ├── 4. ConfigureServices                     // 核心服务注册</span><br><span class="line">    ├── 5. ConfigurePostServices                  // 后置服务注册</span><br><span class="line">    ├── 6. ConfigureMvcBuilder                   // MVC 构建器配置</span><br><span class="line">    ├── 7. ConfigureAutofacContainer             // Autofac 容器配置</span><br><span class="line">    ├── 8. Build + ConfigurePreMiddleware        // 前置中间件</span><br><span class="line">    ├── 9. ConfigureMiddleware                   // 中间件注册</span><br><span class="line">    ├── 10. ConfigurePostMiddleware              // 后置中间件</span><br><span class="line">    └── 11. Run                                  // 启动应用</span><br></pre></td></tr></table></figure><hr><h2 id="启动项目"><a href="#启动项目" class="headerlink" title="启动项目"></a>启动项目</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Program.cs</span></span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> HostApp();</span><br><span class="line">app.Run(<span class="keyword">args</span>, <span class="keyword">typeof</span>(Program).Assembly);</span><br></pre></td></tr></table></figure><hr><h2 id="HostAppOptions-扩展点"><a href="#HostAppOptions-扩展点" class="headerlink" title="HostAppOptions 扩展点"></a>HostAppOptions 扩展点</h2><p>通过在启动代码中配置 <code>HostAppOptions</code> 实现自定义扩展。</p><h3 id="服务注册阶段"><a href="#服务注册阶段" class="headerlink" title="服务注册阶段"></a>服务注册阶段</h3><table><thead><tr><th>回调</th><th>说明</th></tr></thead><tbody><tr><td><code>ConfigurePreWebApplicationBuilder</code></td><td>在创建 WebApplicationBuilder 之前配置</td></tr><tr><td><code>ConfigureWebApplicationBuilder</code></td><td>配置 WebApplicationBuilder</td></tr><tr><td><code>ConfigurePreServices</code></td><td>前置服务注册</td></tr><tr><td><code>ConfigureServices</code></td><td><strong>核心服务注册阶段</strong></td></tr><tr><td><code>ConfigurePostServices</code></td><td>后置服务注册</td></tr><tr><td><code>ConfigureMvcBuilder</code></td><td>配置 MVC 构建器</td></tr><tr><td><code>ConfigureAutofacContainer</code></td><td>配置 Autofac DI 容器</td></tr></tbody></table><h3 id="中间件阶段"><a href="#中间件阶段" class="headerlink" title="中间件阶段"></a>中间件阶段</h3><table><thead><tr><th>回调</th><th>说明</th></tr></thead><tbody><tr><td><code>ConfigurePreMiddleware</code></td><td>前置中间件注册</td></tr><tr><td><code>ConfigureMiddleware</code></td><td>中间件注册</td></tr><tr><td><code>ConfigurePostMiddleware</code></td><td>后置中间件注册</td></tr></tbody></table><h3 id="基础设施阶段"><a href="#基础设施阶段" class="headerlink" title="基础设施阶段"></a>基础设施阶段</h3><table><thead><tr><th>回调</th><th>说明</th></tr></thead><tbody><tr><td><code>ConfigureFreeSqlBuilder</code></td><td>配置 FreeSql 构建器</td></tr><tr><td><code>ConfigurePreFreeSql</code></td><td>前置 FreeSql 配置</td></tr><tr><td><code>ConfigureFreeSql</code></td><td>FreeSql 配置</td></tr><tr><td><code>ConfigureFreeSqlSyncStructure</code></td><td>FreeSql 同步结构配置</td></tr><tr><td><code>ConfigureDynamicApi</code></td><td>动态 API 配置</td></tr><tr><td><code>ConfigureSwaggerUI</code></td><td>Swagger UI 配置</td></tr><tr><td><code>ConfigureIdGenerator</code></td><td>雪花 Id 生成器配置</td></tr><tr><td><code>CustomInitDb</code></td><td>自定义数据库初始化（跳过默认）</td></tr></tbody></table><hr><h2 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> hostAppOptions = <span class="keyword">new</span> HostAppOptions</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 自定义服务注册</span></span><br><span class="line">    ConfigurePreServices = context =&gt;</span><br><span class="line">    &#123;</span><br><span class="line">        context.Services.AddSingletonimyservice,();</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 自定义中间件</span></span><br><span class="line">    ConfigurePreMiddleware = context =&gt;</span><br><span class="line">    &#123;</span><br><span class="line">        context.App.UseMyCustomMiddleware();</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 自定义 FreeSql 配置</span></span><br><span class="line">    ConfigureFreeSql = (fsql, dbConfig) =&gt;</span><br><span class="line">    &#123;</span><br><span class="line">        fsql.Aop.CommandBefore += (s, e) =&gt;</span><br><span class="line">        &#123;</span><br><span class="line">            Console.WriteLine(e.Command.CommandText);</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 自定义数据库初始化</span></span><br><span class="line">    CustomInitDb = <span class="literal">true</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> HostApp();</span><br><span class="line">app.Run(<span class="keyword">args</span>, <span class="keyword">typeof</span>(Program).Assembly, hostAppOptions);</span><br></pre></td></tr></table></figure><hr><h2 id="AppInfo-全局访问"><a href="#AppInfo-全局访问" class="headerlink" title="AppInfo 全局访问"></a>AppInfo 全局访问</h2><p><code>AppInfo</code> 提供全局静态访问，在任意位置获取服务。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取服务</span></span><br><span class="line"><span class="keyword">var</span> cache = AppInfo.GetServiceicachetool();</span><br><span class="line"><span class="keyword">var</span> config = AppInfo.GetRequiredServiceappconfig();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取配置项</span></span><br><span class="line"><span class="keyword">var</span> appConfig = AppInfo.GetOptionsappconfig();</span><br><span class="line"><span class="keyword">var</span> dbConfig = AppInfo.GetOptionsdbconfig();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取当前用户信息</span></span><br><span class="line"><span class="keyword">var</span> userId = AppInfo.User?.Id;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取 HttpContext</span></span><br><span class="line"><span class="keyword">var</span> httpContext = AppInfo.HttpContext;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取日志</span></span><br><span class="line">AppInfo.Log?.LogInformation(<span class="string">&quot;应用启动&quot;</span>);</span><br></pre></td></tr></table></figure><blockquote><p>[!WARNING]<br><code>AppInfo</code> 依赖应用启动后初始化完成，在 <code>Program.cs</code> 的早期阶段可能为 <code>null</code>。</p></blockquote><hr><h2 id="ModuleInfo-模块信息"><a href="#ModuleInfo-模块信息" class="headerlink" title="ModuleInfo 模块信息"></a>ModuleInfo 模块信息</h2><p>用于标识模块程序集和本地化类型。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> ModuleInfo</span><br><span class="line">&#123;</span><br><span class="line">    Assembly = <span class="keyword">typeof</span>(MyModuleService).Assembly,</span><br><span class="line">    LocalizerType = <span class="keyword">typeof</span>(MyModuleLocalizer)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>#中台 #中台&#x2F;DI生命周期  #中台&#x2F;数据库配置 #中台&#x2F;新建接口项目</p>]]>
    </content>
    <id>https://zizai.cc/posts/app-startup-extensions/</id>
    <link href="https://zizai.cc/posts/app-startup-extensions/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>命名空间：ZhonTai.Admin.Core</summary>
    <title>应用启动与扩展</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="配置" scheme="https://zizai.cc/tags/%E9%85%8D%E7%BD%AE/"/>
    <content>
      <![CDATA[<p>Admin.Core 源码项目默认使用 <strong>SQLite</strong> 数据库。项目启动后，数据库文件会自动生成在以下目录：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin\Debug\net10.0</span><br></pre></td></tr></table></figure><p>如需使用其他数据库，可修改配置文件：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ConfigCenter/DbConfig.json</span><br></pre></td></tr></table></figure><p>下面以 <strong>MySQL 创建 bizdb 业务库</strong> 为例说明配置方式。其他数据库的操作方式类似。以下配置均位于：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DbConfig.json -&gt; DbConfig</span><br></pre></td></tr></table></figure><hr><h2 id="安装数据库管理工具"><a href="#安装数据库管理工具" class="headerlink" title="安装数据库管理工具"></a>安装数据库管理工具</h2><p>推荐使用 <strong>Beekeeper Studio</strong> 作为数据库管理工具。</p><ol><li>访问 Beekeeper Studio 官网。</li><li>点击 <strong>Download for Windows</strong>。</li><li>在下载页面点击 <strong>Skip to the download</strong>。</li><li>选择适合当前系统的版本，下载并安装。</li></ol><hr><h2 id="创建数据库"><a href="#创建数据库" class="headerlink" title="创建数据库"></a>创建数据库</h2><p>以 MySQL 创建 <code>bizdb</code> 数据库为例：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="comment">// 监听同步结构脚本，默认不监听</span></span><br><span class="line">  <span class="attr">&quot;syncStructureSql&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 开启建库</span></span><br><span class="line">  <span class="attr">&quot;createDb&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 建库连接字符串</span></span><br><span class="line">  <span class="comment">// 修改 Server=localhost; Port=3306; Uid=root; Pwd=pwd</span></span><br><span class="line">  <span class="comment">// 不要修改 Database=mysql，只有通过默认系统库才能连接数据库并执行建库脚本</span></span><br><span class="line">  <span class="attr">&quot;createDbConnectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 建库脚本，复杂建库脚本可放到 createdbsql.txt 中</span></span><br><span class="line">  <span class="attr">&quot;createDbSql&quot;</span><span class="punctuation">:</span> <span class="string">&quot;CREATE DATABASE `bizdb` CHARACTER SET &#x27;utf8mb4&#x27; COLLATE &#x27;utf8mb4_general_ci&#x27;&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="提示"><a href="#提示" class="headerlink" title="提示"></a>提示</h3><p>FreeSql 数据库连接字符串示例：</p><p><a href="https://freesql.net/guide/#connectionstrings">https://freesql.net/guide/#connectionstrings</a></p><p>第三方数据库连接字符串示例：</p><p><a href="https://www.connectionstrings.com/">https://www.connectionstrings.com</a></p><hr><h2 id="SQLite-数据库配置"><a href="#SQLite-数据库配置" class="headerlink" title="SQLite 数据库配置"></a>SQLite 数据库配置</h2><p>SQLite 数据库不需要配置建库，只需配置连接字符串，项目启动时会自动创建数据库文件。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Sqlite&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;connectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Data Source=|DataDirectory|\\bizdb.db; Pooling=true;Min Pool Size=1&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>SQLite 数据库文件会创建在以下目录：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin\Debug\net10.0\bizdb.db</span><br></pre></td></tr></table></figure><hr><h2 id="连接数据库"><a href="#连接数据库" class="headerlink" title="连接数据库"></a>连接数据库</h2><p>以 MySQL 为例：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="comment">// 数据库类型</span></span><br><span class="line">  <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;MySql&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// default_authentication_plugin = caching_sha2_password</span></span><br><span class="line">  <span class="comment">// MySql &quot;Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// default_authentication_plugin = mysql_native_password</span></span><br><span class="line">  <span class="comment">// MySql &quot;Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;SslMode=none;Min pool size=1;Allow User Variables=True&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 连接字符串</span></span><br><span class="line">  <span class="comment">// 修改 Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd;</span></span><br><span class="line">  <span class="attr">&quot;connectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 指定程序集，连接异常或使用 TIDB 时需要配置该项</span></span><br><span class="line">  <span class="comment">// FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySqlConnector</span></span><br><span class="line">  <span class="attr">&quot;providerType&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h3><p>MySQL 8.0 及以上版本，<code>default_authentication_plugin</code> 默认为 <code>caching_sha2_password</code>，推荐使用以下连接字符串：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True</span><br></pre></td></tr></table></figure><hr><h2 id="同步结构和初始化数据"><a href="#同步结构和初始化数据" class="headerlink" title="同步结构和初始化数据"></a>同步结构和初始化数据</h2><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="comment">// 程序集名称，自动获取实体表；为空则通过 ConfigureFreeSql 自定义配置</span></span><br><span class="line">  <span class="attr">&quot;assemblyNames&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span> <span class="string">&quot;MyCompanyName.MySys.Api&quot;</span> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 指定实体数据库列表，不填则同步所有数据库表实体</span></span><br><span class="line">  <span class="attr">&quot;includeEntityDbs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 排除实体数据库列表，指定不同步的数据库表实体</span></span><br><span class="line">  <span class="attr">&quot;excludeEntityDbs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步结构</span></span><br><span class="line">  <span class="attr">&quot;syncStructure&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步结构批次实体数</span></span><br><span class="line">  <span class="attr">&quot;syncStructureEntityBatchSize&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步数据分批处理大小，默认每次处理 500 条，过大可能导致内存溢出</span></span><br><span class="line">  <span class="attr">&quot;syncDataBatchSize&quot;</span><span class="punctuation">:</span> <span class="number">500</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步数据</span></span><br><span class="line">  <span class="attr">&quot;syncData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步更新数据，谨慎开启，不需要时可以关闭</span></span><br><span class="line">  <span class="attr">&quot;syncUpdateData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步数据地址，默认 InitData/App，一般不调整</span></span><br><span class="line">  <span class="comment">// &quot;SyncDataPath&quot;: &quot;InitData/App&quot;,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步所有表：</span></span><br><span class="line">  <span class="comment">// [&quot;base_dict_type&quot;, &quot;base_dict&quot;, &quot;base_user&quot;, &quot;base_user_staff&quot;, &quot;base_org&quot;, &quot;base_role&quot;, &quot;base_api&quot;, &quot;base_view&quot;, &quot;base_permission&quot;, &quot;base_permission_api&quot;, &quot;base_user_role&quot;, &quot;base_user_org&quot;, &quot;base_role_permission&quot;, &quot;base_tenant&quot;, &quot;base_tenant_permission&quot;]</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步指定表：</span></span><br><span class="line">  <span class="comment">// [&quot;base_api&quot;, &quot;base_view&quot;, &quot;base_permission&quot;, &quot;base_permission_api&quot;]</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步数据包含表，指定表同步；不填则同步所有表</span></span><br><span class="line">  <span class="attr">&quot;syncDataIncludeTables&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步排除表，例如 [&quot;base_user&quot;]</span></span><br><span class="line">  <span class="comment">// 指定表不同步</span></span><br><span class="line">  <span class="attr">&quot;syncDataExcludeTables&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步数据操作用户</span></span><br><span class="line">  <span class="attr">&quot;syncDataUser&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">161223411986501</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;userName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;管理员&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;tenantId&quot;</span><span class="punctuation">:</span> <span class="number">161223412138053</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><hr><h1 id="多数据库配置"><a href="#多数据库配置" class="headerlink" title="多数据库配置"></a>多数据库配置</h1><h2 id="配置示例"><a href="#配置示例" class="headerlink" title="配置示例"></a>配置示例</h2><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="comment">// 多数据库</span></span><br><span class="line">  <span class="attr">&quot;dbs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="comment">// 数据库注册键：模块库</span></span><br><span class="line">      <span class="attr">&quot;key&quot;</span><span class="punctuation">:</span> <span class="string">&quot;moduledb&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 程序集名称，自动获取实体表</span></span><br><span class="line">      <span class="attr">&quot;assemblyNames&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span> <span class="string">&quot;MyCompanyName.MyModuleName.Api.Contracts&quot;</span> <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 指定实体数据库列表，不填则同步所有数据库表实体</span></span><br><span class="line">      <span class="attr">&quot;includeEntityDbs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 排除实体数据库列表，指定不同步的数据库表实体</span></span><br><span class="line">      <span class="attr">&quot;excludeEntityDbs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 监听所有操作</span></span><br><span class="line">      <span class="attr">&quot;monitorCommand&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 监听 CRUD 操作</span></span><br><span class="line">      <span class="attr">&quot;curd&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 监听同步结构脚本</span></span><br><span class="line">      <span class="attr">&quot;syncStructureSql&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 监听同步数据 CRUD 操作</span></span><br><span class="line">      <span class="attr">&quot;syncDataCurd&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 建库</span></span><br><span class="line">      <span class="attr">&quot;createDb&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 建库连接字符串</span></span><br><span class="line">      <span class="comment">// 支持：SqlServer、PostgreSQL、Oracle、OdbcOracle、OdbcSqlServer、OdbcMySql、OdbcPostgreSQL、Odbc、OdbcDameng、MsAccess</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// default_authentication_plugin = caching_sha2_password</span></span><br><span class="line">      <span class="comment">// MySql &quot;Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// default_authentication_plugin = mysql_native_password</span></span><br><span class="line">      <span class="comment">// MySql &quot;Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;SslMode=none;Min pool size=1;Allow User Variables=True&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// SqlServer &quot;Data Source=.;User Id=sa;Password=pwd;Initial Catalog=master;TrustServerCertificate=true;Pooling=true;Min Pool Size=1&quot;</span></span><br><span class="line">      <span class="comment">// PostgreSQL &quot;Host=localhost;Port=5432;Username=postgres;Password=; Database=postgres;Pooling=true;Minimum Pool Size=1&quot;</span></span><br><span class="line">      <span class="comment">// Oracle &quot;user id=SYS;password=pwd; data source=127.0.0.1:1521/XE;Pooling=true;Min Pool Size=1&quot;</span></span><br><span class="line">      <span class="attr">&quot;createDbConnectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 建库脚本，复杂建库脚本可放到 createdbsql.txt 中</span></span><br><span class="line">      <span class="comment">// MySql &quot;CREATE DATABASE `moduledb` CHARACTER SET &#x27;utf8mb4&#x27; COLLATE &#x27;utf8mb4_general_ci&#x27;&quot;</span></span><br><span class="line">      <span class="comment">// SqlServer &quot;CREATE DATABASE [moduledb]&quot;</span></span><br><span class="line">      <span class="comment">// PostgreSQL &quot;CREATE DATABASE \&quot;moduledb\&quot; WITH ENCODING = &#x27;UTF8&#x27;&quot;</span></span><br><span class="line">      <span class="attr">&quot;createDbSql&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步结构</span></span><br><span class="line">      <span class="attr">&quot;syncStructure&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步结构批次实体数</span></span><br><span class="line">      <span class="attr">&quot;syncStructureEntityBatchSize&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步数据分批处理大小，默认每次处理 500 条，过大可能导致内存溢出</span></span><br><span class="line">      <span class="attr">&quot;syncDataBatchSize&quot;</span><span class="punctuation">:</span> <span class="number">500</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步数据</span></span><br><span class="line">      <span class="attr">&quot;syncData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步更新数据</span></span><br><span class="line">      <span class="comment">// 生产环境请谨慎开启。确认需要以数据包更新表数据时再开启。</span></span><br><span class="line">      <span class="comment">// 如不想更新某些表数据，可先配置 syncDataExcludeTables，再执行数据更新操作。</span></span><br><span class="line">      <span class="attr">&quot;syncUpdateData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步数据地址</span></span><br><span class="line">      <span class="attr">&quot;SyncDataPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;InitData/App&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步所有表：</span></span><br><span class="line">      <span class="comment">// [&quot;base_dict_type&quot;, &quot;base_dict&quot;, &quot;base_user&quot;, &quot;base_user_staff&quot;, &quot;base_org&quot;, &quot;base_role&quot;, &quot;base_api&quot;, &quot;base_view&quot;, &quot;base_permission&quot;, &quot;base_permission_api&quot;, &quot;base_user_role&quot;, &quot;base_user_org&quot;, &quot;base_role_permission&quot;, &quot;base_tenant&quot;, &quot;base_tenant_permission&quot;]</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步指定表：</span></span><br><span class="line">      <span class="comment">// [&quot;base_api&quot;, &quot;base_view&quot;, &quot;base_permission&quot;, &quot;base_permission_api&quot;]</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步数据包含表，指定表同步；不填则同步所有表</span></span><br><span class="line">      <span class="attr">&quot;syncDataIncludeTables&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步排除表，例如 [&quot;base_user&quot;]</span></span><br><span class="line">      <span class="comment">// 指定表不同步</span></span><br><span class="line">      <span class="attr">&quot;syncDataExcludeTables&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 同步数据操作用户</span></span><br><span class="line">      <span class="attr">&quot;syncDataUser&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span> <span class="number">161223411986501</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;userName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;管理员&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;tenantId&quot;</span><span class="punctuation">:</span> <span class="number">161223412138053</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 项目初始化时不启用数据生成。</span></span><br><span class="line">      <span class="comment">// 发布生产环境前，如果开发环境存在配置数据需要更新数据包，可以开启生成数据包。</span></span><br><span class="line">      <span class="comment">// 使用完成后请关闭。</span></span><br><span class="line">      <span class="comment">// 开启生成数据前，请先关闭 syncStructure、syncData、createDb。</span></span><br><span class="line">      <span class="attr">&quot;generateData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 数据库配置：https://github.com/dotnetcore/FreeSql/wiki/入门</span></span><br><span class="line">      <span class="comment">// 连接字符串语法：https://www.connectionstrings.com</span></span><br><span class="line">      <span class="comment">// 数据库类型：</span></span><br><span class="line">      <span class="comment">// MySql = 0, SqlServer = 1, PostgreSQL = 2, Oracle = 3, Sqlite = 4,</span></span><br><span class="line">      <span class="comment">// OdbcOracle = 5, OdbcSqlServer = 6, OdbcMySql = 7, OdbcPostgreSQL = 8,</span></span><br><span class="line">      <span class="comment">// Odbc = 9, OdbcDameng = 10, MsAccess = 11, Dameng = 12,</span></span><br><span class="line">      <span class="comment">// OdbcKingbaseES = 13, ShenTong = 14, KingbaseES = 15, Firebird = 16</span></span><br><span class="line">      <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Sqlite&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 连接字符串</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// default_authentication_plugin = caching_sha2_password</span></span><br><span class="line">      <span class="comment">// MySql &quot;Server=localhost; Port=3306; Database=moduledb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// default_authentication_plugin = mysql_native_password</span></span><br><span class="line">      <span class="comment">// MySql &quot;Server=localhost; Port=3306; Database=moduledb; Uid=root; Pwd=pwd; Charset=utf8mb4;SslMode=none;Min pool size=1;Allow User Variables=True&quot;</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// SqlServer &quot;Data Source=.;Integrated Security=True;Initial Catalog=moduledb;Pooling=true;Min Pool Size=1&quot;</span></span><br><span class="line">      <span class="comment">// PostgreSQL &quot;Host=localhost;Port=5432;Username=postgres;Password=; Database=moduledb;Pooling=true;Minimum Pool Size=1&quot;</span></span><br><span class="line">      <span class="comment">// Sqlite &quot;Data Source=|DataDirectory|\\moduledb.db; Pooling=true;Min Pool Size=1&quot;</span></span><br><span class="line">      <span class="comment">// Oracle &quot;user id=SYS;password=pwd; data source=127.0.0.1:1521/XE;Pooling=true;Min Pool Size=1&quot;</span></span><br><span class="line">      <span class="attr">&quot;connectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Data Source=|DataDirectory|\\moduledb.db; Pooling=true;Min Pool Size=1&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 指定程序集</span></span><br><span class="line">      <span class="comment">// FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySqlConnector</span></span><br><span class="line">      <span class="attr">&quot;providerType&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// 读写分离从库列表</span></span><br><span class="line">      <span class="attr">&quot;slaveList&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="comment">// &#123;</span></span><br><span class="line">        <span class="comment">//   // 权重</span></span><br><span class="line">        <span class="comment">//   &quot;Weight&quot;: 1,</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">//   // 连接字符串</span></span><br><span class="line">        <span class="comment">//   &quot;ConnectionString&quot;: &quot;Data Source=|DataDirectory|\\moduledb.db; Pooling=true;Min Pool Size=1&quot;</span></span><br><span class="line">        <span class="comment">// &#125;</span></span><br><span class="line">      <span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><hr><h2 id="定义数据库键名"><a href="#定义数据库键名" class="headerlink" title="定义数据库键名"></a>定义数据库键名</h2><p>在以下目录中新建 <code>DbKeys.cs</code> 类：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api.Core/Consts</span><br></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.ComponentModel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Core.Consts</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 数据库键名</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">DbKeys</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 模块库注册键</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">Description(<span class="string">&quot;模块库注册键&quot;</span>)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">string</span> ModuleDb &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125; = <span class="string">&quot;moduledb&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中，<code>ModuleDb</code> 为模块库注册键，可根据需要自定义命名。</p><hr><h2 id="新建模块基础仓储类"><a href="#新建模块基础仓储类" class="headerlink" title="新建模块基础仓储类"></a>新建模块基础仓储类</h2><p>在以下目录中新建 <code>ModuleRepositoryBase.cs</code> 类：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api/Core/Repositories</span><br></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> MyCompanyName.MySys.Api.Core.Consts;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Db.Transaction;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Repositories;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MySys.Api.Core.Repositories</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块库基础仓储</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;typeparam name=&quot;TEntity&quot;&gt;</span><span class="doctag">&lt;/typeparam&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleRepositoryBase</span>&lt;<span class="title">TEntity</span>&gt; : <span class="title">RepositoryBase</span>&lt;<span class="title">TEntity</span>&gt; <span class="keyword">where</span> <span class="title">TEntity</span> : <span class="keyword">class</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleRepositoryBase</span>(<span class="params">UnitOfWorkManagerCloud uowm</span>) : <span class="title">base</span>(<span class="params">DbKeys.ModuleDb, uowm</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="配置宿主应用"><a href="#配置宿主应用" class="headerlink" title="配置宿主应用"></a>配置宿主应用</h2><p>在 <code>Program.cs</code> 中配置宿主应用：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 宿主应用实例</span></span><br><span class="line"><span class="keyword">new</span> HostApp(<span class="keyword">new</span> HostAppOptions</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 配置前置服务</span></span><br><span class="line">    ConfigurePreServices = context =&gt;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 设置模块库注册键</span></span><br><span class="line">        DbKeys.ModuleDb = <span class="string">&quot;moduledb&quot;</span>;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 配置 Autofac 容器</span></span><br><span class="line">    ConfigureAutofacContainer = (builder, context) =&gt;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 注册泛型模块基础仓储类</span></span><br><span class="line">        builder.RegisterGeneric(<span class="keyword">typeof</span>(ModuleRepositoryBase&lt;&gt;))</span><br><span class="line">            .InstancePerLifetimeScope()</span><br><span class="line">            .PropertiesAutowired();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><hr><h1 id="使用多数据库"><a href="#使用多数据库" class="headerlink" title="使用多数据库"></a>使用多数据库</h1><h2 id="方式一：使用-ModuleRepositoryBase-泛型模块基础仓储类"><a href="#方式一：使用-ModuleRepositoryBase-泛型模块基础仓储类" class="headerlink" title="方式一：使用 ModuleRepositoryBase 泛型模块基础仓储类"></a>方式一：使用 <code>ModuleRepositoryBase</code> 泛型模块基础仓储类</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Order(1010)</span>]</span><br><span class="line">[<span class="meta">DynamicApi(Area = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">BaseService</span>, <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> ModuleRepositoryBase&lt;Entity&gt; _moduleRepo;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params">ModuleRepositoryBase&lt;Entity&gt; moduleRepo</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleRepo = moduleRepo;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="方式二：使用-FreeSqlCloud"><a href="#方式二：使用-FreeSqlCloud" class="headerlink" title="方式二：使用 FreeSqlCloud"></a>方式二：使用 <code>FreeSqlCloud</code></h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">BaseService</span>, <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> FreeSqlCloud _freeSqlCloud;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params">FreeSqlCloud freeSqlCloud</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _freeSqlCloud = freeSqlCloud;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">GetData</span>()</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> fsql = _freeSqlCloud.Use(DbKeys.ModuleDb);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="方式三：使用模块仓储接口"><a href="#方式三：使用模块仓储接口" class="headerlink" title="方式三：使用模块仓储接口"></a>方式三：使用模块仓储接口</h2><h3 id="定义-IModuleRepository-模块仓储接口"><a href="#定义-IModuleRepository-模块仓储接口" class="headerlink" title="定义 IModuleRepository 模块仓储接口"></a>定义 <code>IModuleRepository</code> 模块仓储接口</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> MyCompanyName.MySys.Api.Contracts.Domain.Module;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Repositories;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MySys.Api.Contracts</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块仓储接口</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">IModuleRepository</span> : <span class="title">IRepositoryBase</span>&lt;<span class="title">ModuleEntity</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="定义-ModuleRepository-模块仓储"><a href="#定义-ModuleRepository-模块仓储" class="headerlink" title="定义 ModuleRepository 模块仓储"></a>定义 <code>ModuleRepository</code> 模块仓储</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> MyCompanyName.MySys.Api.Contracts;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MySys.Api.Contracts.Domain.Module;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MySys.Api.Core.Repositories;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Db.Transaction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MySys.Api.Repositories.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块仓储</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleRepository</span> : <span class="title">ModuleRepositoryBase</span>&lt;<span class="title">ModuleEntity</span>&gt;, <span class="title">IModuleRepository</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleRepository</span>(<span class="params">UnitOfWorkManagerCloud uowm</span>) : <span class="title">base</span>(<span class="params">uowm</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="使用-IModuleRepository-模块仓储接口"><a href="#使用-IModuleRepository-模块仓储接口" class="headerlink" title="使用 IModuleRepository 模块仓储接口"></a>使用 <code>IModuleRepository</code> 模块仓储接口</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Order(1010)</span>]</span><br><span class="line">[<span class="meta">DynamicApi(Area = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">BaseService</span>, <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> IModuleRepository _moduleRepo;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params">IModuleRepository moduleRepo</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleRepo = moduleRepo;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="生成数据"><a href="#生成数据" class="headerlink" title="生成数据"></a>生成数据</h1><p>将 <code>*.json</code> 数据文件生成到以下目录：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">InitData/App</span><br></pre></td></tr></table></figure><p>配置如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="comment">// 建库</span></span><br><span class="line">  <span class="attr">&quot;createDb&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步结构</span></span><br><span class="line">  <span class="attr">&quot;syncStructure&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步数据</span></span><br><span class="line">  <span class="attr">&quot;syncData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步更新数据</span></span><br><span class="line">  <span class="attr">&quot;syncUpdateData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 生成数据</span></span><br><span class="line">  <span class="attr">&quot;generateData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="提示-1"><a href="#提示-1" class="headerlink" title="提示"></a>提示</h2><ul><li>项目开发启动时，不建议开启生成数据。</li><li>发布生产环境前，如果开发环境存在配置数据需要更新数据包，可以开启生成数据包。</li><li>项目发布后，如需更新数据包，也可以开启生成数据包。</li><li>使用完成后，请及时关闭 <code>generateData</code>。</li></ul><hr><h2 id="自定义生成数据"><a href="#自定义生成数据" class="headerlink" title="自定义生成数据"></a>自定义生成数据</h2><p>在以下目录中新建 <code>Data</code> 文件夹，并新增 <code>CustomGenerateData.cs</code> 类：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api/Core/Data</span><br></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Configs;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Db.Data;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MySys.Api.Contracts.Domain.Module;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MySys.Api.Core.Data</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">CustomGenerateData</span> : <span class="title">GenerateData</span>, <span class="title">IGenerateData</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">async</span> Task <span class="title">GenerateDataAsync</span>(<span class="params">IFreeSql db, AppConfig appConfig</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> isTenant = appConfig.Tenant;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> modules = <span class="keyword">await</span> db.Queryable&lt;ModuleEntity&gt;().ToListAsync();</span><br><span class="line"></span><br><span class="line">        SaveDataToJsonFile&lt;ModuleEntity&gt;(modules, isTenant, path: <span class="string">&quot;InitData/App&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="同步数据"><a href="#同步数据" class="headerlink" title="同步数据"></a>同步数据</h1><p>将以下目录中的 JSON 数据同步到数据库：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">InitData/App/*.json</span><br></pre></td></tr></table></figure><p>配置如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="comment">// 同步结构</span></span><br><span class="line">  <span class="attr">&quot;syncStructure&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步数据</span></span><br><span class="line">  <span class="attr">&quot;syncData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 同步更新数据</span></span><br><span class="line">  <span class="attr">&quot;syncUpdateData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 生成数据</span></span><br><span class="line">  <span class="attr">&quot;generateData&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><hr><h2 id="自定义同步数据"><a href="#自定义同步数据" class="headerlink" title="自定义同步数据"></a>自定义同步数据</h2><p>在以下目录中新建 <code>Data</code> 文件夹，并新增 <code>CustomSyncData.cs</code> 类：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api/Core/Data</span><br></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Configs;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Db.Data;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MySys.Api.Contracts.Domain.Module;</span><br><span class="line"><span class="keyword">using</span> System.Linq;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MySys.Api.Core.Data</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">CustomSyncData</span> : <span class="title">SyncData</span>, <span class="title">ISyncData</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">async</span> Task <span class="title">SyncDataAsync</span>(<span class="params">IFreeSql db, DbConfig dbConfig = <span class="literal">null</span>, AppConfig appConfig = <span class="literal">null</span></span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">using</span> <span class="keyword">var</span> unitOfWork = db.CreateUnitOfWork();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">var</span> isTenant = appConfig.Tenant;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">await</span> SyncEntityAsync&lt;ModuleEntity&gt;(</span><br><span class="line">                db,</span><br><span class="line">                unitOfWork,</span><br><span class="line">                dbConfig,</span><br><span class="line">                appConfig,</span><br><span class="line">                whereFunc: (<span class="keyword">select</span>, batchDataList) =&gt;</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">if</span> (appConfig.Tenant)</span><br><span class="line">                    &#123;</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">select</span>.Where(a =&gt;</span><br><span class="line">                            batchDataList.Any(b =&gt;</span><br><span class="line">                                a.Id == b.Id ||</span><br><span class="line">                                (</span><br><span class="line">                                    a.TenantId == b.TenantId &amp;&amp;</span><br><span class="line">                                    !<span class="built_in">string</span>.IsNullOrWhiteSpace(a.Name) &amp;&amp;</span><br><span class="line">                                    a.Name == b.Name</span><br><span class="line">                                )</span><br><span class="line">                            )</span><br><span class="line">                        );</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">select</span>.Where(a =&gt;</span><br><span class="line">                        batchDataList.Any(b =&gt;</span><br><span class="line">                            a.Id == b.Id ||</span><br><span class="line">                            (</span><br><span class="line">                                !<span class="built_in">string</span>.IsNullOrWhiteSpace(a.Name) &amp;&amp;</span><br><span class="line">                                a.Name == b.Name</span><br><span class="line">                            )</span><br><span class="line">                        )</span><br><span class="line">                    );</span><br><span class="line">                &#125;,</span><br><span class="line">                insertDataFunc: (batchDataList, dbDataList) =&gt;</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">return</span> batchDataList.Where(a =&gt; !dbDataList.Any(b =&gt; a.Id == b.Id));</span><br><span class="line">                &#125;</span><br><span class="line">            );</span><br><span class="line"></span><br><span class="line">            unitOfWork.Commit();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Exception)</span><br><span class="line">        &#123;</span><br><span class="line">            unitOfWork.Rollback();</span><br><span class="line">            <span class="keyword">throw</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h1 id="数据库事务"><a href="#数据库事务" class="headerlink" title="数据库事务"></a>数据库事务</h1><h2 id="使用事务"><a href="#使用事务" class="headerlink" title="使用事务"></a>使用事务</h2><p>在服务方法上添加 <code>[AppTransaction]</code> 特性，即可使用当前项目主库事务。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">AppTransaction</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">virtual</span> <span class="keyword">async</span> Task <span class="title">UpdateAsync</span>(<span class="params">UserUpdateInput input</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (!(input?.Id &gt; <span class="number">0</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 事务回滚</span></span><br><span class="line">        <span class="keyword">throw</span> ResultOutput.Exception(<span class="string">&quot;请选择用户&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    <span class="keyword">var</span> user = <span class="keyword">await</span> _userRepository.GetAsync(input.Id);</span><br><span class="line">    <span class="keyword">if</span> (!(user?.Id &gt; <span class="number">0</span>))</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 事务回滚</span></span><br><span class="line">        <span class="keyword">throw</span> ResultOutput.Exception(<span class="string">&quot;用户不存在&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 也可以使用：</span></span><br><span class="line">        <span class="comment">// throw new AppException(&quot;用户不存在&quot;);</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 数据映射</span></span><br><span class="line">    _mapper.Map(input, user);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 更新用户</span></span><br><span class="line">    <span class="keyword">await</span> _userRepository.UpdateAsync(user);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除用户角色</span></span><br><span class="line">    <span class="keyword">await</span> _userRoleRepository.DeleteAsync(a =&gt; a.UserId == user.Id);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (input.RoleIds != <span class="literal">null</span> &amp;&amp; input.RoleIds.Any())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> roles = input.RoleIds.Select(a =&gt; <span class="keyword">new</span> UserRoleEntity</span><br><span class="line">        &#123;</span><br><span class="line">            UserId = user.Id,</span><br><span class="line">            RoleId = a</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 批量插入用户角色</span></span><br><span class="line">        <span class="keyword">await</span> _userRoleRepository.InsertAsync(roles);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="提示-2"><a href="#提示-2" class="headerlink" title="提示"></a>提示</h3><ul><li>事务可跨方法使用。</li><li>支持同步方法和异步方法。</li><li>如果服务是动态 API，开启事务的方法需要定义为 <code>virtual</code> 虚方法，事务才能正常生效。</li><li>存在多个 CUD 操作时，事务会确保操作要么全部成功，要么全部失败。</li></ul><hr><h2 id="事务回滚"><a href="#事务回滚" class="headerlink" title="事务回滚"></a>事务回滚</h2><p>推荐抛出友好异常触发事务回滚：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">throw</span> ResultOutput.Exception(msg);</span><br></pre></td></tr></table></figure><p>也可以使用：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> AppException(msg);</span><br></pre></td></tr></table></figure><hr><h2 id="提交事务"><a href="#提交事务" class="headerlink" title="提交事务"></a>提交事务</h2><p>如果方法执行过程中没有异常抛出，事务将自动提交。</p><hr><h2 id="事务属性"><a href="#事务属性" class="headerlink" title="事务属性"></a>事务属性</h2><table><thead><tr><th>参数</th><th>说明</th><th>类型</th><th>默认值</th></tr></thead><tbody><tr><td><code>Propagation</code></td><td>事务传播方式</td><td><code>enum</code></td><td><code>Required</code></td></tr><tr><td><code>IsolationLevel</code></td><td>事务隔离级别</td><td><code>enum</code></td><td><code>-</code></td></tr></tbody></table><hr><h2 id="Propagation-事务传播方式"><a href="#Propagation-事务传播方式" class="headerlink" title="Propagation 事务传播方式"></a><code>Propagation</code> 事务传播方式</h2><table><thead><tr><th>值</th><th>说明</th></tr></thead><tbody><tr><td><code>Required</code></td><td>如果当前没有事务，则新建事务；如果已存在事务，则加入当前事务。默认选择。</td></tr><tr><td><code>Supports</code></td><td>支持当前事务；如果没有当前事务，则以非事务方式执行。</td></tr><tr><td><code>Mandatory</code></td><td>使用当前事务；如果没有当前事务，则抛出异常。</td></tr><tr><td><code>NotSupported</code></td><td>以非事务方式执行；如果当前存在事务，则挂起当前事务。</td></tr><tr><td><code>Never</code></td><td>以非事务方式执行；如果当前存在事务，则抛出异常。</td></tr><tr><td><code>Nested</code></td><td>以嵌套事务方式执行。</td></tr></tbody></table><hr><h2 id="IsolationLevel-事务隔离级别"><a href="#IsolationLevel-事务隔离级别" class="headerlink" title="IsolationLevel 事务隔离级别"></a><code>IsolationLevel</code> 事务隔离级别</h2><table><thead><tr><th>值</th><th>说明</th></tr></thead><tbody><tr><td><code>Chaos</code></td><td>无法覆盖隔离级别更高的事务中挂起的更改。</td></tr><tr><td><code>ReadCommitted</code></td><td>读取数据时保持共享锁，以避免脏读；但事务结束前数据仍可能被更改，从而导致不可重复读或幻读。</td></tr><tr><td><code>ReadUncommitted</code></td><td>允许脏读，不发布共享锁，也不接受独占锁。</td></tr><tr><td><code>RepeatableRead</code></td><td>对查询中使用的所有数据加锁，防止其他用户更新这些数据。可防止不可重复读，但仍可能出现幻读。</td></tr><tr><td><code>Serializable</code></td><td>在数据集上放置范围锁，防止事务完成前其他用户更新行或插入行。</td></tr><tr><td><code>Snapshot</code></td><td>通过在一个应用程序修改数据时保存另一个应用程序可读取的数据版本来减少阻塞。表示当前事务无法看到其他事务中的更改，即使重新查询也是如此。</td></tr><tr><td><code>Unspecified</code></td><td>正在使用与指定隔离级别不同的隔离级别，但无法确定具体级别。</td></tr></tbody></table><p>#中台 #中台&#x2F;.NET模板 #中台&#x2F;搭建项目框架 #中台&#x2F;数据库配置 #中台&#x2F;多数据库配置</p>]]>
    </content>
    <id>https://zizai.cc/posts/admin-core-db-config/</id>
    <link href="https://zizai.cc/posts/admin-core-db-config/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>Admin.Core 源码项目默认使用 SQLite 数据库。项目启动后，数据库文件会自动生成在以下目录：</summary>
    <title>Admin.Core 数据库配置说明</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="配置" scheme="https://zizai.cc/tags/%E9%85%8D%E7%BD%AE/"/>
    <category term="国际化" scheme="https://zizai.cc/tags/%E5%9B%BD%E9%99%85%E5%8C%96/"/>
    <content>
      <![CDATA[<p>本文档介绍如何在接口项目中启用和配置内置国际化功能。</p><h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Resources/</span><br><span class="line">├─ MyModuleLocalizer.cs</span><br><span class="line">├─ Resources.MyModuleLocalizer.en.json</span><br><span class="line">└─ Resources.MyModuleLocalizer.zh-TW.json</span><br></pre></td></tr></table></figure><h2 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h2><p>在 <code>ConfigCenter/appconfig.json</code> 中添加语言配置：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;AppConfig&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Lang&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;Enable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;DefaultLang&quot;</span><span class="punctuation">:</span> <span class="string">&quot;zh-CN&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;Langs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;zh-CN&quot;</span><span class="punctuation">,</span> <span class="string">&quot;zh-TW&quot;</span><span class="punctuation">,</span> <span class="string">&quot;en&quot;</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;RequestCultureProviders&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;AcceptLanguageHeader&quot;</span><span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="配置项说明"><a href="#配置项说明" class="headerlink" title="配置项说明"></a>配置项说明</h3><table><thead><tr><th>配置项</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>Enable</code></td><td>bool</td><td>是否启用国际化，默认<code>true</code></td></tr><tr><td><code>DefaultLang</code></td><td>string</td><td>默认语言</td></tr><tr><td><code>Langs</code></td><td>string[]</td><td>支持的语言列表</td></tr><tr><td><code>RequestCultureProviders</code></td><td>string[]</td><td>请求语言解析器，可选值：<code>QueryString</code>、<code>Cookie</code>、<code>AcceptLanguageHeader</code>，设置空数组 <code>[]</code> 则强制使用默认语言</td></tr></tbody></table><h2 id="翻译文件配置"><a href="#翻译文件配置" class="headerlink" title="翻译文件配置"></a>翻译文件配置</h2><h3 id="英文翻译"><a href="#英文翻译" class="headerlink" title="英文翻译"></a>英文翻译</h3><p>创建 <code>Resources.MyModuleLocalizer.en.json</code>：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;中文键&quot;</span><span class="punctuation">:</span> <span class="string">&quot;English description&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;&#123;0&#125;中文键&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&#123;0&#125;English description&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><blockquote><p>[!TIP]<br>推荐使用中文作为语言索引键，便于维护和查找。</p></blockquote><h3 id="繁体翻译"><a href="#繁体翻译" class="headerlink" title="繁体翻译"></a>繁体翻译</h3><p>创建 <code>Resources.MyModuleLocalizer.zh-TW.json</code>：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;中文键&quot;</span><span class="punctuation">:</span> <span class="string">&quot;中文鍵&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="语言资源类"><a href="#语言资源类" class="headerlink" title="语言资源类"></a>语言资源类</h2><p>创建共享语言资源类 <code>MyModuleLocalizer.cs</code>：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> Microsoft.Extensions.Localization;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Attributes;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Resources;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Resources</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块国际化</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">InjectSingleton</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MyModuleLocalizer</span> : <span class="title">ModuleLocalizer</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyModuleLocalizer</span>(<span class="params">IStringLocalizer&lt;MyModuleLocalizer&gt; localizer</span>) : <span class="title">base</span>(<span class="params">localizer</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Order(20)</span>]</span><br><span class="line">[<span class="meta">DynamicApi(Area = AppConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">BaseService</span>, <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> MyModuleLocalizer _myModuleLocalizer;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params">MyModuleLocalizer myModuleLocalizer</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _myModuleLocalizer = myModuleLocalizer;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 修改</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">UpdateAsync</span>(<span class="params">ModuleUpdateInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (!(input?.Id &gt; <span class="number">0</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">throw</span> ResultOutput.Exception(</span><br><span class="line">                _myModuleLocalizer[<span class="string">&quot;&#123;0&#125;中文键&quot;</span>, <span class="string">&quot;动态字符串参数&quot;</span>]</span><br><span class="line">            );</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="调用方式"><a href="#调用方式" class="headerlink" title="调用方式"></a>调用方式</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 静态翻译</span></span><br><span class="line">_myModuleLocalizer[<span class="string">&quot;欢迎&quot;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment">// 动态参数翻译（索引从 0 开始）</span></span><br><span class="line">_myModuleLocalizer[<span class="string">&quot;&#123;0&#125;用户，&#123;1&#125;消息&quot;</span>, <span class="string">&quot;张三&quot;</span>, <span class="string">&quot;登录成功&quot;</span>]</span><br></pre></td></tr></table></figure><p>#中台 #中台&#x2F;新建接口项目 #中台&#x2F;配置文件 #中台&#x2F;特性注解</p>]]>
    </content>
    <id>https://zizai.cc/posts/backend-i18n-guide/</id>
    <link href="https://zizai.cc/posts/backend-i18n-guide/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>本文档介绍如何在接口项目中启用和配置内置国际化功能。</summary>
    <title>后端国际化配置指南</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="缓存" scheme="https://zizai.cc/tags/%E7%BC%93%E5%AD%98/"/>
    <content>
      <![CDATA[<p>通过合理的缓存管理，可以有效提升系统性能并降低数据库访问压力。</p><h2 id="1-缓存配置"><a href="#1-缓存配置" class="headerlink" title="1. 缓存配置"></a>1. 缓存配置</h2><p>缓存配置采用 JSON 格式定义，支持 <strong>Memory</strong> 和 <strong>Redis</strong> 两种缓存类型。</p><p><strong>Redis 配置示例：</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;CacheConfig&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="comment">// 缓存类型：Memory = 0, Redis = 1</span></span><br><span class="line">    <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Redis&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="comment">// Redis 配置</span></span><br><span class="line">    <span class="attr">&quot;redis&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="comment">// 连接字符串</span></span><br><span class="line">      <span class="attr">&quot;connectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;127.0.0.1:6379,password=pwd,defaultDatabase=0&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>type</code></td><td>缓存类型，支持<code>Memory</code>（内存缓存）和 <code>Redis</code>（分布式缓存）</td></tr><tr><td><code>redis.connectionString</code></td><td>Redis 连接字符串，包含主机地址、端口、密码和默认数据库等信息</td></tr></tbody></table><h2 id="2-缓存键定义"><a href="#2-缓存键定义" class="headerlink" title="2. 缓存键定义"></a>2. 缓存键定义</h2><p>缓存键用于唯一标识缓存数据。建议将缓存键定义为常量，并提供辅助方法以统一键的生成规则。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 缓存键定义</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">CacheKeys</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 模块缓存键，格式：module:action:&#123;id&#125;</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">Description(<span class="string">&quot;模块缓存键&quot;</span>)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">const</span> <span class="built_in">string</span> ModuleActionKey = <span class="string">&quot;module:action:&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 根据模块 ID 生成缓存键</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;id&quot;&gt;</span>模块 ID<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="built_in">string</span> <span class="title">GetModuleActionKey</span>(<span class="params"><span class="built_in">long</span> id</span>)</span> =&gt; <span class="string">$&quot;<span class="subst">&#123;ModuleActionKey&#125;</span><span class="subst">&#123;id&#125;</span>&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>缓存键命名规范：</strong></p><ul><li>采用冒号分隔的层级结构，如 <code>{entity}:{action}:{id}</code></li><li>统一使用常量定义，便于维护和避免硬编码</li></ul><h2 id="3-缓存接口（ICacheTool）"><a href="#3-缓存接口（ICacheTool）" class="headerlink" title="3. 缓存接口（ICacheTool）"></a>3. 缓存接口（ICacheTool）</h2><p><code>ICacheTool</code> 接口提供通用的缓存操作方法，包括：</p><table><thead><tr><th>方法</th><th>说明</th></tr></thead><tbody><tr><td><code>GetAsync&lt;T&gt;</code></td><td>根据键获取缓存值</td></tr><tr><td><code>SetAsync</code></td><td>设置缓存，支持过期时间</td></tr><tr><td><code>DelAsync</code></td><td>删除指定键的缓存</td></tr><tr><td><code>ExistsAsync</code></td><td>判断键是否存在</td></tr><tr><td><code>KeysAsync</code></td><td>批量获取匹配的键</td></tr></tbody></table><h2 id="4-缓存使用示例"><a href="#4-缓存使用示例" class="headerlink" title="4. 缓存使用示例"></a>4. 缓存使用示例</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块服务</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">Order(1010)</span>]</span><br><span class="line">[<span class="meta">DynamicApi(Area = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> AppRepositoryBase&lt;ModuleEntity&gt; _moduleRep;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> ICacheTool _cache;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params">AppRepositoryBase&lt;ModuleEntity&gt; moduleRep, ICacheTool cache</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleRep = moduleRep;</span><br><span class="line">        _cache = cache;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 查询模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> cacheKey = CacheKeys.GetModuleActionKey(id);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 优先从缓存获取</span></span><br><span class="line">        <span class="keyword">var</span> module = <span class="keyword">await</span> _cache.GetAsync&lt;ModuleGetOutput&gt;(cacheKey);</span><br><span class="line">        <span class="keyword">if</span> (module == <span class="literal">null</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 缓存未命中，从数据库查询</span></span><br><span class="line">            module = <span class="keyword">await</span> _moduleRep.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">            <span class="comment">// 并写入缓存，过期时间 5 分钟</span></span><br><span class="line">            <span class="keyword">await</span> _cache.SetAsync(cacheKey, module, TimeSpan.FromMinutes(<span class="number">5</span>));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> module;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 修改模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">UpdateAsync</span>(<span class="params">ModuleUpdateInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> entity = <span class="keyword">await</span> _moduleRep.GetAsync(input.Id);</span><br><span class="line">        <span class="keyword">if</span> (entity?.Id <span class="keyword">is</span> <span class="keyword">not</span> &gt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> ResultOutput.Exception(<span class="string">&quot;模块不存在&quot;</span>);</span><br><span class="line"></span><br><span class="line">        Mapper.Map(input, entity);</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.UpdateAsync(entity);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 同步更新缓存</span></span><br><span class="line">        <span class="keyword">var</span> cacheKey = CacheKeys.GetModuleActionKey(input.Id);</span><br><span class="line">        <span class="keyword">await</span> _cache.SetAsync(cacheKey, Mapper.Map&lt;ModuleGetOutput&gt;(entity), TimeSpan.FromMinutes(<span class="number">5</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 彻底删除模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">DeleteAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.DeleteAsync(m =&gt; m.Id == id);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 删除缓存，避免脏数据</span></span><br><span class="line">        <span class="keyword">var</span> cacheKey = CacheKeys.GetModuleActionKey(id);</span><br><span class="line">        <span class="keyword">await</span> _cache.DelAsync(cacheKey);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="缓存策略说明"><a href="#缓存策略说明" class="headerlink" title="缓存策略说明"></a>缓存策略说明</h3><table><thead><tr><th>操作</th><th>缓存行为</th></tr></thead><tbody><tr><td><strong>查询（GetAsync）</strong></td><td>缓存优先，miss 时回源数据库并回填缓存</td></tr><tr><td><strong>修改（UpdateAsync）</strong></td><td>先更新数据库，再同步更新缓存</td></tr><tr><td><strong>删除（DeleteAsync）</strong></td><td>先删除数据库记录，再删除缓存</td></tr></tbody></table><h3 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h3><ul><li><strong>性能提升</strong>：减少数据库访问频率，加快查询响应</li><li><strong>数据一致性</strong>：修改&#x2F;删除时同步更新缓存，避免脏读</li><li><strong>灵活过期</strong>：通过设置 TTL 避免缓存数据过期</li></ul><h3 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h3><ul><li>频繁读取但较少更新的数据（如模块配置、字典项）</li><li>对查询响应时间要求较高的业务场景</li><li>需承受一定数据延迟的业务场景</li></ul><p>#缓存管理 #中台&#x2F;配置文件 #中台&#x2F;公共帮助类 #Redis #Memory</p>]]>
    </content>
    <id>https://zizai.cc/posts/cache-management/</id>
    <link href="https://zizai.cc/posts/cache-management/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>通过合理的缓存管理，可以有效提升系统性能并降低数据库访问压力。</summary>
    <title>缓存管理</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <content>
      <![CDATA[<hr><h2 id="一、接口文档-AccessToken-配置"><a href="#一、接口文档-AccessToken-配置" class="headerlink" title="一、接口文档 AccessToken 配置"></a>一、接口文档 AccessToken 配置</h2><h3 id="步骤一：关闭验证码"><a href="#步骤一：关闭验证码" class="headerlink" title="步骤一：关闭验证码"></a>步骤一：关闭验证码</h3><p>在项目中打开 <code>Configs/appconfig.json</code> 文件，将 <code>varifyCode.enable</code> 设置为 <code>false</code>。</p><h3 id="步骤二：获取-AccessToken"><a href="#步骤二：获取-AccessToken" class="headerlink" title="步骤二：获取 AccessToken"></a>步骤二：获取 AccessToken</h3><ol><li>在接口文档中选择中台 <strong>Admin</strong></li><li>打开 <strong>认证授权服务 → 登录接口</strong></li><li>点击调试，在 <strong>raw</strong> 选项下输入以下内容：</li></ol><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;userName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;password&quot;</span><span class="punctuation">:</span> <span class="string">&quot;123asd&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;passwordKey&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;captchaId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;captchaData&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><blockquote><p>发送请求后，响应体中返回的 <code>accessToken</code> 即为所需的认证令牌。</p></blockquote><h3 id="步骤三：添加全局参数"><a href="#步骤三：添加全局参数" class="headerlink" title="步骤三：添加全局参数"></a>步骤三：添加全局参数</h3><ol><li>打开 <strong>文档管理 → 全局参数设置</strong></li><li>点击 <strong>添加参数</strong>，配置如下：</li></ol><table><thead><tr><th>参数名称</th><th>Authorization</th></tr></thead><tbody><tr><td><strong>参数值</strong></td><td><code>Bearer AccessToken</code>（注意 <code>Bearer</code> 与令牌之间有一个空格）</td></tr><tr><td><strong>参数类型</strong></td><td><code>header</code></td></tr></tbody></table><h3 id="步骤四：验证配置"><a href="#步骤四：验证配置" class="headerlink" title="步骤四：验证配置"></a>步骤四：验证配置</h3><p>关闭所有已打开的接口窗口，重新访问测试接口。如果请求头中出现了已配置的参数，说明配置成功。</p><hr><h2 id="二、项目发布后缺少-FreeSql-数据库实现包"><a href="#二、项目发布后缺少-FreeSql-数据库实现包" class="headerlink" title="二、项目发布后缺少 FreeSql 数据库实现包"></a>二、项目发布后缺少 FreeSql 数据库实现包</h2><h3 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h3><p>Admin.Core 源码仅在 <strong>Debug 开发环境</strong>下引用了 FreeSql 数据库实现包，导致发布后启动接口时报错：</p><blockquote><p>缺少 Freesql 数据库实现包：FreeSql.Provider.MySql.dll</p></blockquote><h3 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h3><ol><li>单击 <strong>ZhonTai.Host</strong> 项目</li><li>将 Debug 开发环境下的 <code>FreeSql.Provider.MySql</code> 安装包<strong>剪切</strong>到生产环境中</li><li>重新发布项目</li></ol><h3 id="配置变更示例"><a href="#配置变更示例" class="headerlink" title="配置变更示例"></a>配置变更示例</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 修改前（仅 Debug 环境可用） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">ItemGroup</span> <span class="attr">Condition</span>=<span class="string">&quot;&#x27;$(Configuration)&#x27;==&#x27;Debug&#x27;&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">&quot;FreeSql.Provider.MySql&quot;</span> <span class="attr">Version</span>=<span class="string">&quot;3.2.825&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 修改后（所有环境可用） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">&quot;FreeSql.Provider.MySql&quot;</span> <span class="attr">Version</span>=<span class="string">&quot;3.2.825&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="三、默认管理员账号密码"><a href="#三、默认管理员账号密码" class="headerlink" title="三、默认管理员账号密码"></a>三、默认管理员账号密码</h2><table><thead><tr><th>项目</th><th>内容</th></tr></thead><tbody><tr><td><strong>账号</strong></td><td><code>admin</code></td></tr><tr><td><strong>密码</strong></td><td><code>123asd</code></td></tr></tbody></table><p>新增用户的默认初始密码可在 <code>appconfig.json</code> 的 <code>defaultPassword</code> 配置项中修改。</p><hr><h2 id="四、查看角色菜单权限时报错"><a href="#四、查看角色菜单权限时报错" class="headerlink" title="四、查看角色菜单权限时报错"></a>四、查看角色菜单权限时报错</h2><h3 id="问题描述-1"><a href="#问题描述-1" class="headerlink" title="问题描述"></a>问题描述</h3><p>查看角色菜单权限时提示 <strong>“服务器内部错误”</strong>，错误日志中可见：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Parameter &#x27;@cte_pid&#x27; must be defined.</span><br></pre></td></tr></table></figure><h3 id="原因分析"><a href="#原因分析" class="headerlink" title="原因分析"></a>原因分析</h3><p>该查询使用了<strong>数据库递归函数</strong>，而 <strong>MySQL 5.x</strong> 版本不支持该特性。</p><h3 id="解决方案-1"><a href="#解决方案-1" class="headerlink" title="解决方案"></a>解决方案</h3><ul><li>方案一：升级 MySQL 至 <strong>8.0+</strong> 版本</li><li>方案二：更换为支持递归查询的数据库（如 PostgreSQL）进行测试</li></ul><hr><h2 id="五、如何禁用或启用仓储过滤器"><a href="#五、如何禁用或启用仓储过滤器" class="headerlink" title="五、如何禁用或启用仓储过滤器"></a>五、如何禁用或启用仓储过滤器</h2><h3 id="常用场景"><a href="#常用场景" class="headerlink" title="常用场景"></a>常用场景</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 仅禁用删除过滤器</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> _ = _yourRep.DataFilter.Disable(FilterNames.Delete);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 仅开启删除过滤器（需先禁用全部再启用）</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> _ = _yourRep.DataFilter.DisableAll();</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> __ = _yourRep.DataFilter.Enable(FilterNames.Delete);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 禁用租户过滤器</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> _ = _yourRep.DataFilter.Disable(FilterNames.Tenant);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 禁用所有过滤器</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> _ = _yourRep.DataFilter.DisableAll();</span><br></pre></td></tr></table></figure><blockquote><p>使用 <code>using</code> 语句可确保过滤器在代码块结束时自动恢复，无需手动重置。</p></blockquote><p>#中台&#x2F;常见问题 #中台&#x2F;数据库配置 #仓储模式 #API接口 #数据安全</p>]]>
    </content>
    <id>https://zizai.cc/posts/backend-faq/</id>
    <link href="https://zizai.cc/posts/backend-faq/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>在项目中打开 Configs/appconfig.json 文件，将 varifyCode.enable 设置为 false。</summary>
    <title>后台常见问题</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="数据安全" scheme="https://zizai.cc/tags/%E6%95%B0%E6%8D%AE%E5%AE%89%E5%85%A8/"/>
    <content>
      <![CDATA[<p><strong>命名空间</strong>：<code>ZhonTai.Common.Helpers</code></p><p>提供数据脱敏、IP 获取与验证、密码规则校验等安全相关工具类。</p><hr><h2 id="DataMaskHelper-数据脱敏"><a href="#DataMaskHelper-数据脱敏" class="headerlink" title="DataMaskHelper 数据脱敏"></a>DataMaskHelper 数据脱敏</h2><h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>用户列表中手机号&#x2F;邮箱脱敏展示</li><li>日志中 IP 地址脱敏</li><li>数据导出时敏感信息保护</li></ul><h3 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 手机号脱敏（中间4位替换为 ****）</span></span><br><span class="line"><span class="keyword">var</span> phone = DataMaskHelper.PhoneMask(<span class="string">&quot;13812345678&quot;</span>);</span><br><span class="line"><span class="comment">// &quot;138****5678&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义脱敏字符</span></span><br><span class="line"><span class="keyword">var</span> phone2 = DataMaskHelper.PhoneMask(<span class="string">&quot;13812345678&quot;</span>, <span class="string">&quot;***&quot;</span>);</span><br><span class="line"><span class="comment">// &quot;138***5678&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 邮箱脱敏</span></span><br><span class="line"><span class="keyword">var</span> email = DataMaskHelper.EmailMask(<span class="string">&quot;zhangsan@example.com&quot;</span>);</span><br><span class="line"><span class="comment">// &quot;zh****@example.com&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// IP 地址脱敏</span></span><br><span class="line"><span class="keyword">var</span> ip = DataMaskHelper.IPMask(<span class="string">&quot;192.168.1.100&quot;</span>);</span><br><span class="line"><span class="comment">// &quot;192.*.*.100&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义 IP 脱敏字符</span></span><br><span class="line"><span class="keyword">var</span> ip2 = DataMaskHelper.IPMask(<span class="string">&quot;192.168.1.100&quot;</span>, <span class="string">&quot;x&quot;</span>);</span><br><span class="line"><span class="comment">// &quot;192.x.x.100&quot;</span></span><br></pre></td></tr></table></figure><h3 id="内置正则表达式"><a href="#内置正则表达式" class="headerlink" title="内置正则表达式"></a>内置正则表达式</h3><p>可直接使用以下内置正则：</p><table><thead><tr><th>正则</th><th>说明</th></tr></thead><tbody><tr><td><code>DataMaskHelper.PhoneMaskRegex</code></td><td>手机号脱敏正则</td></tr><tr><td><code>DataMaskHelper.EmailMaskRegex</code></td><td>邮箱脱敏正则</td></tr><tr><td><code>DataMaskHelper.IPMaskRegex</code></td><td>IP 脱敏正则</td></tr></tbody></table><hr><h2 id="IPHelper-IP-工具"><a href="#IPHelper-IP-工具" class="headerlink" title="IPHelper IP 工具"></a>IPHelper IP 工具</h2><h3 id="应用场景-1"><a href="#应用场景-1" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>从 HTTP 请求中获取客户端真实 IP</li><li>IP 地址格式校验</li></ul><h3 id="使用示例-1"><a href="#使用示例-1" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 校验 IP 格式</span></span><br><span class="line">IPHelper.IsIP(<span class="string">&quot;192.168.1.1&quot;</span>);    <span class="comment">// true</span></span><br><span class="line">IPHelper.IsIP(<span class="string">&quot;256.1.1.1&quot;</span>);      <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 从 HttpRequest 获取真实 IP（支持代理场景）</span></span><br><span class="line"><span class="keyword">var</span> ip = IPHelper.GetIP(HttpContext.Request);</span><br></pre></td></tr></table></figure><h3 id="GetIP-优先级顺序"><a href="#GetIP-优先级顺序" class="headerlink" title="GetIP 优先级顺序"></a>GetIP 优先级顺序</h3><p>按以下优先级获取 IP 地址：</p><ol><li><code>X-Real-IP</code> 请求头</li><li><code>X-Forwarded-For</code> 请求头</li><li><code>RemoteIpAddress</code></li></ol><p>默认返回 <code>127.0.0.1</code></p><hr><h2 id="PasswordHelper-密码校验"><a href="#PasswordHelper-密码校验" class="headerlink" title="PasswordHelper 密码校验"></a>PasswordHelper 密码校验</h2><h3 id="应用场景-2"><a href="#应用场景-2" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>用户注册&#x2F;修改密码时的密码强度校验</li><li>表单密码规则验证</li></ul><h3 id="校验规则"><a href="#校验规则" class="headerlink" title="校验规则"></a>校验规则</h3><table><thead><tr><th>规则</th><th>说明</th></tr></thead><tbody><tr><td>长度</td><td>6-16 位</td></tr><tr><td>字母</td><td>必须包含至少一个字母</td></tr><tr><td>数字</td><td>必须包含至少一个数字</td></tr><tr><td>特殊字符</td><td>支持<code>!@#$%^&amp;.*</code></td></tr></tbody></table><h3 id="使用示例-2"><a href="#使用示例-2" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 校验密码</span></span><br><span class="line">PasswordHelper.Verify(<span class="string">&quot;abc123&quot;</span>);           <span class="comment">// true</span></span><br><span class="line">PasswordHelper.Verify(<span class="string">&quot;Abc@123&quot;</span>);          <span class="comment">// true</span></span><br><span class="line">PasswordHelper.Verify(<span class="string">&quot;123456&quot;</span>);            <span class="comment">// false（缺少字母）</span></span><br><span class="line">PasswordHelper.Verify(<span class="string">&quot;abcdef&quot;</span>);            <span class="comment">// false（缺少数字）</span></span><br><span class="line">PasswordHelper.Verify(<span class="string">&quot;12345&quot;</span>);             <span class="comment">// false（少于 6 位）</span></span><br><span class="line">PasswordHelper.Verify(<span class="string">&quot;12345678901234567&quot;</span>); <span class="comment">// false（超过 16 位）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用内置正则</span></span><br><span class="line"><span class="keyword">var</span> isValid = System.Text.RegularExpressions.Regex.IsMatch(</span><br><span class="line">    <span class="string">&quot;Abc@123&quot;</span>,</span><br><span class="line">    PasswordHelper.PasswordRegex</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>#中台&#x2F;公共帮助类 #数据脱敏 #密码校验 #正则表达式 #数据安全</p>]]>
    </content>
    <id>https://zizai.cc/posts/data-security-tools/</id>
    <link href="https://zizai.cc/posts/data-security-tools/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>命名空间：ZhonTai.Common.Helpers</summary>
    <title>数据安全工具</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <content>
      <![CDATA[<h2 id="1-目标"><a href="#1-目标" class="headerlink" title="1. 目标"></a>1. 目标</h2><p><code>DataTagTest</code> 用于保存测试标签采集数据。采集数据按 <code>CollectTime</code> 做月分表，写入数据前由业务服务主动同步当前月份及未来月份的物理表，避免程序长期运行后因为未来月份表不存在导致插入失败。</p><p>当前设计不在程序启动时自动建表，建表动作由 <code>DataTagTestService.AddAsync</code> 在写入前触发。</p><h2 id="2-相关文件"><a href="#2-相关文件" class="headerlink" title="2. 相关文件"></a>2. 相关文件</h2><table><thead><tr><th>文件</th><th>作用</th></tr></thead><tbody><tr><td><code>NPP.IOT.Api.Contracts/Domain/DataCollection/DataTagTestEntity.cs</code></td><td>定义采集数据实体、表名模板、分表规则和索引</td></tr><tr><td><code>NPP.IOT.Api/Core/Helper/DateShardingTableHelper.cs</code></td><td>公共日期分表同步 Helper</td></tr><tr><td><code>NPP.IOT.Api/Services/DataTagTest/DataTagTestService.cs</code></td><td>写入采集数据前调用 Helper 同步物理分表</td></tr><tr><td><code>NPP.IOT.Host/Program.cs</code></td><td>当前不再在启动阶段自动创建<code>DataTagTest</code> 分表</td></tr></tbody></table><h2 id="3-实体设计"><a href="#3-实体设计" class="headerlink" title="3. 实体设计"></a>3. 实体设计</h2><p>实体类：<code>DataTagTestEntity</code></p><p>位置：<code>NPP.IOT.Api.Contracts/Domain/DataCollection/DataTagTestEntity.cs</code></p><p>核心配置：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Table(Name = DbConsts.TableNamePrefix + <span class="string">&quot;tag_test_&#123;yyyyMM&#125;&quot;</span>, AsTable = <span class="string">&quot;CollectTime=2026-1-1(1 month)&quot;</span>)</span>]</span><br><span class="line">[<span class="meta">Index(<span class="string">&quot;idx_&#123;tablename&#125;_01&quot;</span>, nameof(TagId))</span>]</span><br><span class="line">[<span class="meta">Index(<span class="string">&quot;idx_&#123;tablename&#125;_02&quot;</span>, nameof(TagCode))</span>]</span><br><span class="line">[<span class="meta">Index(<span class="string">&quot;idx_&#123;tablename&#125;_03&quot;</span>, nameof(GatewayCode))</span>]</span><br><span class="line">[<span class="meta">Index(<span class="string">&quot;idx_&#123;tablename&#125;_04&quot;</span>, nameof(CollectTime))</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">DataTagTestEntity</span> : <span class="title">EntityData</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">long</span> TagId &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> TagCode &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> GatewayCode &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Value &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span>? ValueType &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="keyword">public</span> DateTime CollectTime &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>说明：</p><ul><li><code>Name = DbConsts.TableNamePrefix + &quot;tag_test_{yyyyMM}&quot;</code> 定义物理表名模板。</li><li><code>DbConsts.TableNamePrefix</code> 当前是 <code>iot_</code>。</li><li>实际表名形如：</li></ul><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">iot_tag_test_202601</span><br><span class="line">iot_tag_test_202602</span><br><span class="line">iot_tag_test_202603</span><br></pre></td></tr></table></figure><ul><li><code>AsTable = &quot;CollectTime=2026-1-1(1 month)&quot;</code> 表示按 <code>CollectTime</code> 字段从 <code>2026-01-01</code> 开始，每 1 个月分一张表。</li><li><code>CollectTime</code> 是分表字段，也是写入时决定落到哪张物理表的关键字段。</li></ul><h2 id="4-Helper-设计"><a href="#4-Helper-设计" class="headerlink" title="4. Helper 设计"></a>4. Helper 设计</h2><p>公共 Helper：<code>DateShardingTableHelper</code></p><p>位置：<code>NPP.IOT.Api/Core/Helper/DateShardingTableHelper.cs</code></p><p>设计目的：</p><ul><li>不把分表同步逻辑写死在 <code>DataTagTestService</code> 中。</li><li>不再使用 <code>DataTagTestTableSyncer</code> 这种只服务单个实体的同步器。</li><li>后续其他按日期分表的实体也可以复用同一个 Helper。</li></ul><p>当前代码：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> FreeSql;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">NPP.IOT.Api.Core.Helper</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 日期分表同步帮助类</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">DateShardingTableHelper</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 同步当前月和指定未来月份范围的物理分表</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">SyncFutureMonthlyTables</span>&lt;<span class="title">TEntity</span>&gt;(<span class="params">IFreeSql db, DateTime baseTime, <span class="built_in">int</span> futureMonthCount</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        SyncMonthlyTables&lt;TEntity&gt;(db, baseTime, futureMonthCount + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 同步指定月份范围的物理分表</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">SyncMonthlyTables</span>&lt;<span class="title">TEntity</span>&gt;(<span class="params">IFreeSql db, DateTime startMonth, <span class="built_in">int</span> monthCount</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (monthCount &lt;= <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> entityType = <span class="keyword">typeof</span>(TEntity);</span><br><span class="line">        <span class="keyword">var</span> table = db.CodeFirst.GetTableByEntity(entityType);</span><br><span class="line">        <span class="keyword">var</span> month = ToMonth(startMonth);</span><br><span class="line">        <span class="keyword">var</span> tableNames = <span class="keyword">new</span> List&lt;<span class="built_in">string</span>&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; monthCount; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">var</span> tableName = table.AsTableImpl.GetTableNameByColumnValue(month.AddMonths(i), autoExpand: <span class="literal">true</span>);</span><br><span class="line">            tableNames.Add(tableName);</span><br><span class="line">            <span class="keyword">if</span> (!db.DbFirst.ExistsTable(tableName))</span><br><span class="line">            &#123;</span><br><span class="line">                db.CodeFirst.SyncStructure(entityType, tableName);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        table.AsTableImpl.SetDefaultAllTables(_ =&gt; tableNames.ToArray());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> DateTime <span class="title">ToMonth</span>(<span class="params">DateTime <span class="keyword">value</span></span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> DateTime(<span class="keyword">value</span>.Year, <span class="keyword">value</span>.Month, <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-1-SyncFutureMonthlyTables-的含义"><a href="#4-1-SyncFutureMonthlyTables-的含义" class="headerlink" title="4.1 SyncFutureMonthlyTables 的含义"></a>4.1 <code>SyncFutureMonthlyTables</code> 的含义</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DateShardingTableHelper.SyncFutureMonthlyTables&lt;DataTagTestEntity&gt;(_db, input.CollectTime, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>含义是：</p><ul><li>以 <code>input.CollectTime</code> 所在月份为基准。</li><li>同步当前月。</li><li>再同步未来 <code>futureMonthCount</code> 个月。</li></ul><p>例如：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">baseTime = <span class="keyword">new</span> DateTime(<span class="number">2028</span>, <span class="number">3</span>, <span class="number">15</span>);</span><br><span class="line">futureMonthCount = <span class="number">1</span>;</span><br></pre></td></tr></table></figure><p>会同步：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">iot_tag_test_202803</span><br><span class="line">iot_tag_test_202804</span><br></pre></td></tr></table></figure><p>如果希望同步当前月 + 未来 3 个月，则调用：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DateShardingTableHelper.SyncFutureMonthlyTables&lt;DataTagTestEntity&gt;(_db, input.CollectTime, <span class="number">3</span>);</span><br></pre></td></tr></table></figure><p>对应表：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">iot_tag_test_202803</span><br><span class="line">iot_tag_test_202804</span><br><span class="line">iot_tag_test_202805</span><br><span class="line">iot_tag_test_202806</span><br></pre></td></tr></table></figure><h3 id="4-2-SyncMonthlyTables-的执行流程"><a href="#4-2-SyncMonthlyTables-的执行流程" class="headerlink" title="4.2 SyncMonthlyTables 的执行流程"></a>4.2 <code>SyncMonthlyTables</code> 的执行流程</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DateShardingTableHelper.SyncMonthlyTables&lt;DataTagTestEntity&gt;(_db, startMonth, monthCount);</span><br></pre></td></tr></table></figure><p>执行步骤：</p><ol><li>判断 <code>monthCount &lt;= 0</code> 时直接返回。</li><li>通过 <code>typeof(TEntity)</code> 获取实体类型。</li><li>调用 <code>db.CodeFirst.GetTableByEntity(entityType)</code> 获取 FreeSql 表元数据。</li><li>把传入时间规整到当月 1 号。</li><li>循环 <code>monthCount</code> 次，每次增加 1 个月。</li><li>调用 <code>table.AsTableImpl.GetTableNameByColumnValue(...)</code> 根据实体的 <code>AsTable</code> 配置计算真实物理表名。</li><li>调用 <code>db.DbFirst.ExistsTable(tableName)</code> 判断表是否存在。</li><li>表不存在时调用 <code>db.CodeFirst.SyncStructure(entityType, tableName)</code> 创建物理表。</li><li>最后通过 <code>table.AsTableImpl.SetDefaultAllTables(...)</code> 设置当前实体默认参与查询的物理表范围。</li></ol><h2 id="5-Service-中如何调用生成表"><a href="#5-Service-中如何调用生成表" class="headerlink" title="5. Service 中如何调用生成表"></a>5. Service 中如何调用生成表</h2><p>服务类：<code>DataTagTestService</code></p><p>位置：<code>NPP.IOT.Api/Services/DataTagTest/DataTagTestService.cs</code></p><p>当前构造函数注入：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">readonly</span> IFreeSql _db;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">readonly</span> IDataTagTestRepository _dataTagTestRep;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">DataTagTestService</span>(<span class="params">IFreeSql db, IDataTagTestRepository dataTagTestRep</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    _db = db;</span><br><span class="line">    _dataTagTestRep = dataTagTestRep;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>AddAsync</code> 写入前同步分表：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">AllowAnonymous</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> <span class="title">Task</span>&lt;<span class="title">long</span>&gt; <span class="title">AddAsync</span>(<span class="params">DataTagTestAddInput input</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    DateShardingTableHelper.SyncFutureMonthlyTables&lt;DataTagTestEntity&gt;(_db, input.CollectTime, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> entity = Mapper.Map&lt;DataTagTestEntity&gt;(input);</span><br><span class="line">    <span class="keyword">await</span> _dataTagTestRep.InsertAsync(entity);</span><br><span class="line">    <span class="keyword">return</span> entity.Id;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用顺序：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">接口收到新增请求</span><br><span class="line">        ↓</span><br><span class="line">读取 input.CollectTime</span><br><span class="line">        ↓</span><br><span class="line">调用 DateShardingTableHelper.SyncFutureMonthlyTables&lt;DataTagTestEntity&gt;()</span><br><span class="line">        ↓</span><br><span class="line">确保目标月份及未来月份物理表存在</span><br><span class="line">        ↓</span><br><span class="line">Mapper.Map&lt;DataTagTestEntity&gt;(input)</span><br><span class="line">        ↓</span><br><span class="line">_dataTagTestRep.InsertAsync(entity)</span><br><span class="line">        ↓</span><br><span class="line">FreeSql 根据 CollectTime 写入对应月表</span><br></pre></td></tr></table></figure><p>设计重点：</p><ul><li>必须先同步表，再执行插入。</li><li>分表基准使用 <code>input.CollectTime</code>，不是服务器当前时间。</li><li>这样可以支持补录历史数据、写入未来采集时间等场景。</li></ul><h2 id="6-程序启动时不自动建表"><a href="#6-程序启动时不自动建表" class="headerlink" title="6. 程序启动时不自动建表"></a>6. 程序启动时不自动建表</h2><p>位置：<code>NPP.IOT.Host/Program.cs</code></p><p>当前启动阶段配置：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//配置FreeSql同步结构</span></span><br><span class="line">ConfigureFreeSqlSyncStructure = (freeSql, dbConfig) =&gt;</span><br><span class="line">&#123;</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><p>说明：</p><ul><li>启动时不调用 <code>DateShardingTableHelper</code>。</li><li>启动时不创建 <code>DataTagTest</code> 分表。</li><li>表创建由 <code>DataTagTestService.AddAsync</code> 在写入前按需触发。</li></ul><p>这样可以避免程序启动时一次性创建大量未来表，也避免启动逻辑和某个业务实体强绑定。</p><h2 id="7-如何扩展到其他按日期分表实体"><a href="#7-如何扩展到其他按日期分表实体" class="headerlink" title="7. 如何扩展到其他按日期分表实体"></a>7. 如何扩展到其他按日期分表实体</h2><p>假设新增实体 <code>TagDataEntity</code>，只要实体上配置了 FreeSql 分表规则：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Table(Name = DbConsts.TableNamePrefix + <span class="string">&quot;tag_data_&#123;yyyyMM&#125;&quot;</span>, AsTable = <span class="string">&quot;CollectTime=2026-1-1(1 month)&quot;</span>)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">TagDataEntity</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> DateTime CollectTime &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在对应 Service 写入前调用：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DateShardingTableHelper.SyncFutureMonthlyTables&lt;TagDataEntity&gt;(_db, input.CollectTime, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>或同步固定月份范围：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DateShardingTableHelper.SyncMonthlyTables&lt;TagDataEntity&gt;(_db, <span class="keyword">new</span> DateTime(<span class="number">2028</span>, <span class="number">1</span>, <span class="number">1</span>), <span class="number">12</span>);</span><br></pre></td></tr></table></figure><h2 id="8-注意事项"><a href="#8-注意事项" class="headerlink" title="8. 注意事项"></a>8. 注意事项</h2><ol><li><code>DateShardingTableHelper</code> 依赖实体上的 FreeSql <code>[Table(..., AsTable = ...)]</code> 配置。</li><li><code>SyncFutureMonthlyTables&lt;TEntity&gt;</code> 的 <code>futureMonthCount</code> 表示未来月份数量，不包含当前月。</li><li>当前月会通过 <code>futureMonthCount + 1</code> 一起同步。</li><li><code>SyncMonthlyTables&lt;TEntity&gt;</code> 会调用 <code>SetDefaultAllTables</code>，影响后续当前实体默认查询的物理表范围。</li><li>不建议在 <code>Program.cs</code> 中为单个业务实体写自动建表逻辑。</li><li>插入前同步表可以保证长期运行时新月份表能被创建。</li></ol><h2 id="9-当前-DataTagTest-的完整调用示例"><a href="#9-当前-DataTagTest-的完整调用示例" class="headerlink" title="9. 当前 DataTagTest 的完整调用示例"></a>9. 当前 DataTagTest 的完整调用示例</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> <span class="title">Task</span>&lt;<span class="title">long</span>&gt; <span class="title">AddAsync</span>(<span class="params">DataTagTestAddInput input</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    DateShardingTableHelper.SyncFutureMonthlyTables&lt;DataTagTestEntity&gt;(_db, input.CollectTime, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> entity = Mapper.Map&lt;DataTagTestEntity&gt;(input);</span><br><span class="line">    <span class="keyword">await</span> _dataTagTestRep.InsertAsync(entity);</span><br><span class="line">    <span class="keyword">return</span> entity.Id;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果 <code>input.CollectTime = 2028-03-15</code>，当前配置会确保以下表存在后再插入：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">iot_tag_test_202803</span><br><span class="line">iot_tag_test_202804</span><br></pre></td></tr></table></figure><p>插入时 FreeSql 会根据 <code>CollectTime</code> 把数据写入：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iot_tag_test_202803</span><br></pre></td></tr></table></figure><p>#中台 #中台&#x2F;数据库分表 #FreeSql #API接口 #仓储模式</p>]]>
    </content>
    <id>https://zizai.cc/posts/datatag-sharding-notes/</id>
    <link href="https://zizai.cc/posts/datatag-sharding-notes/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>DataTagTest 用于保存测试标签采集数据。采集数据按 CollectTime 做月分表，写入数据前由业务服务主动同步当前月份及未来月份的物理表，避免程序长期运行后因为未来月份表不存在导致插入失败。</summary>
    <title>DataTagTest 分表设计笔记</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="仓储" scheme="https://zizai.cc/tags/%E4%BB%93%E5%82%A8/"/>
    <category term="依赖注入" scheme="https://zizai.cc/tags/%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5/"/>
    <content>
      <![CDATA[<p><strong>命名空间</strong>：<code>ZhonTai.Admin.Services</code> &#x2F; <code>ZhonTai.Admin.Core.Repositories</code> &#x2F; <code>ZhonTai.Admin.Core.RegisterModules</code></p><p>中台 Admin 采用 <strong>Autofac</strong> 作为 DI 容器，通过约定实现 Service 和 Repository 的自动注册，并提供基类以简化开发流程。</p><hr><h2 id="自动注册规则"><a href="#自动注册规则" class="headerlink" title="自动注册规则"></a>自动注册规则</h2><p><code>RegisterModule</code> 扫描 <code>appconfig.json</code> 中 <code>AssemblyNames</code> 指定的程序集，自动注册满足以下条件的类：</p><h3 id="注册条件"><a href="#注册条件" class="headerlink" title="注册条件"></a>注册条件</h3><ul><li>类名以 <code>Service</code> 或 <code>Repository</code> 结尾</li><li><strong>或</strong> 实现了 <code>IRegisterIOC</code> 接口</li><li>非抽象、非接口、public 访问级别</li><li>未标记 <code>[NonRegisterIOC]</code> 特性</li></ul><h3 id="默认生命周期"><a href="#默认生命周期" class="headerlink" title="默认生命周期"></a>默认生命周期</h3><p>所有自动注册的类默认生命周期为 <code>InstancePerLifetimeScope</code>（Scoped）。</p><h3 id="注册示例"><a href="#注册示例" class="headerlink" title="注册示例"></a>注册示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 符合条件，自动注册</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">UserService</span> : <span class="title">IBaseService</span> &#123; &#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">UserRepository</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 即使无接口，实现 IRegisterIOC 也会自动注册</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MyHelper</span> : <span class="title">IRegisterIOC</span> &#123; &#125;</span><br></pre></td></tr></table></figure><hr><h2 id="生命周期管理"><a href="#生命周期管理" class="headerlink" title="生命周期管理"></a>生命周期管理</h2><p>通过特性可覆盖默认的 Scoped 生命周期。</p><table><thead><tr><th>特性</th><th>生命周期</th><th>适用场景</th></tr></thead><tbody><tr><td><code>[InjectTransient]</code></td><td>瞬时</td><td>每次请求创建新实例，适用于轻量级、无状态的工具类</td></tr><tr><td><code>[InjectScoped]</code></td><td>作用域</td><td>每个 HTTP 请求创建一个实例（<strong>Service&#x2F;Repository 默认值</strong>）</td></tr><tr><td><code>[InjectSingleton]</code></td><td>单例</td><td>全局唯一实例，适用于缓存、配置等共享资源</td></tr></tbody></table><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">InjectTransient</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">EmailHelper</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line">[<span class="meta">InjectScoped</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">OrderService</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line">[<span class="meta">InjectSingleton</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">CacheService</span> &#123; &#125;</span><br></pre></td></tr></table></figure><h3 id="排除自动注册"><a href="#排除自动注册" class="headerlink" title="排除自动注册"></a>排除自动注册</h3><p>标记 <code>[NonRegisterIOC]</code> 的类不会被自动注册，需手动处理：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">NonRegisterIOC</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MyCustomService</span> &#123; &#125;</span><br></pre></td></tr></table></figure><hr><h2 id="BaseService"><a href="#BaseService" class="headerlink" title="BaseService"></a>BaseService</h2><p>服务基类，继承后可直接使用以下属性：</p><table><thead><tr><th>属性</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>Cache</code></td><td><code>ICacheTool</code></td><td>缓存工具</td></tr><tr><td><code>Mapper</code></td><td><code>IMapper</code></td><td>对象映射（Mapster）</td></tr><tr><td><code>User</code></td><td><code>IUser</code></td><td>当前用户信息</td></tr><tr><td><code>Logger</code></td><td><code>ILogger</code></td><td>日志记录</td></tr><tr><td><code>ServiceProvider</code></td><td><code>IServiceProvider</code></td><td>服务提供者</td></tr></tbody></table><h3 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Services;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ArticleService</span> : <span class="title">BaseService</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> AppRepositoryBase&lt;ArticleEntity&gt; _repository;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ArticleService</span>(<span class="params">AppRepositoryBase&lt;ArticleEntity&gt; repository</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _repository = repository;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;IResultOutput&gt; <span class="title">CreateAsync</span>(<span class="params">ArticleCreateInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> entity = Mapper.Map&lt;ArticleEntity&gt;(input);</span><br><span class="line">        <span class="keyword">await</span> _repository.InsertAsync(entity);</span><br><span class="line">        <span class="keyword">return</span> ResultOutput.Ok();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">async</span> Task&lt;IResultOutput&lt;ArticleDto&gt;&gt; GetAsync(<span class="built_in">long</span> id)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> dto = <span class="keyword">await</span> _repository.GetAsync&lt;ArticleDto&gt;(id);</span><br><span class="line">        <span class="keyword">return</span> ResultOutput.Ok(dto);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="RepositoryBase"><a href="#RepositoryBase" class="headerlink" title="RepositoryBase"></a>RepositoryBase</h2><p>仓储基类继承 FreeSQL 的 <code>BaseRepository</code>，提供软删除、递归删除等增强方法。</p><h3 id="注入方式"><a href="#注入方式" class="headerlink" title="注入方式"></a>注入方式</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ArticleService</span> : <span class="title">BaseService</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 使用 AppRepositoryBase（多数据库场景）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> AppRepositoryBase&lt;ArticleEntity&gt; _repository;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ArticleService</span>(<span class="params">AppRepositoryBase&lt;ArticleEntity&gt; repository</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _repository = repository;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="多数据库自定义仓储"><a href="#多数据库自定义仓储" class="headerlink" title="多数据库自定义仓储"></a>多数据库自定义仓储</h3><p>当项目使用多数据库时，需继承 <code>RepositoryBase</code> 创建自定义仓储基类来指定数据库：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Consts;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Db.Transaction;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Repositories;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyApp.Api.Core.Repositories</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">AppRepositoryBase</span>&lt;<span class="title">TEntity</span>&gt; : <span class="title">RepositoryBase</span>&lt;<span class="title">TEntity</span>&gt; <span class="keyword">where</span> <span class="title">TEntity</span> : <span class="keyword">class</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AppRepositoryBase</span>(<span class="params">UnitOfWorkManagerCloud uowm</span>) : <span class="title">base</span>(<span class="params">DbKeys.AppDb, uowm</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="常用方法"><a href="#常用方法" class="headerlink" title="常用方法"></a>常用方法</h3><table><thead><tr><th>方法</th><th>说明</th></tr></thead><tbody><tr><td><code>GetAsync&lt;TDto&gt;(id)</code></td><td>根据 Id 获取 DTO</td></tr><tr><td><code>GetAsync&lt;TDto&gt;(Expression)</code></td><td>根据条件获取 DTO</td></tr><tr><td><code>InsertAsync(entity)</code></td><td>插入数据</td></tr><tr><td><code>UpdateAsync(entity)</code></td><td>更新数据</td></tr><tr><td><code>DeleteAsync(id)</code></td><td>物理删除</td></tr><tr><td><code>SoftDeleteAsync(id)</code></td><td>软删除（设置<code>IsDeleted=true</code>）</td></tr><tr><td><code>SoftDeleteAsync(ids)</code></td><td>批量软删除</td></tr><tr><td><code>SoftDeleteAsync(Expression)</code></td><td>条件软删除</td></tr><tr><td><code>DeleteRecursiveAsync(Expression)</code></td><td>递归物理删除</td></tr><tr><td><code>SoftDeleteRecursiveAsync(Expression)</code></td><td>递归软删除</td></tr><tr><td><code>Select</code></td><td>FreeSQL 查询构造器（支持<code>Where</code>&#x2F;<code>OrderBy</code>&#x2F;<code>ToPage</code> 等）</td></tr></tbody></table><h3 id="软删除示例"><a href="#软删除示例" class="headerlink" title="软删除示例"></a>软删除示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 单个软删除</span></span><br><span class="line"><span class="keyword">await</span> _repository.SoftDeleteAsync(id);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 批量软删除</span></span><br><span class="line"><span class="keyword">await</span> _repository.SoftDeleteAsync(<span class="keyword">new</span> <span class="built_in">long</span>[] &#123; <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> &#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 条件软删除</span></span><br><span class="line"><span class="keyword">await</span> _repository.SoftDeleteAsync(a =&gt; a.Status == <span class="number">-1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 递归软删除（树形结构）</span></span><br><span class="line"><span class="keyword">await</span> _repository.SoftDeleteRecursiveAsync(a =&gt; a.Id == parentId);</span><br></pre></td></tr></table></figure><h3 id="禁用全局过滤器"><a href="#禁用全局过滤器" class="headerlink" title="禁用全局过滤器"></a>禁用全局过滤器</h3><p>软删除、租户等特性通过 FreeSQL 全局过滤器实现。需要时可临时禁用：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">await</span> _repository.SoftDeleteAsync(</span><br><span class="line">    a =&gt; a.Id == id,</span><br><span class="line">    FilterNames.Delete,   <span class="comment">// 禁用软删除过滤</span></span><br><span class="line">    FilterNames.Tenant     <span class="comment">// 禁用租户过滤</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure><h3 id="内置全局过滤器"><a href="#内置全局过滤器" class="headerlink" title="内置全局过滤器"></a>内置全局过滤器</h3><table><thead><tr><th>过滤器名</th><th>说明</th></tr></thead><tbody><tr><td><code>FilterNames.Delete</code></td><td>过滤<code>IsDeleted=false</code> 的数据</td></tr><tr><td><code>FilterNames.Tenant</code></td><td>过滤当前租户数据</td></tr><tr><td><code>FilterNames.Data</code></td><td>数据权限过滤</td></tr><tr><td><code>FilterNames.Self</code></td><td>仅本人数据</td></tr><tr><td><code>FilterNames.Member</code></td><td>会员数据过滤</td></tr></tbody></table><hr><h2 id="懒加载服务"><a href="#懒加载服务" class="headerlink" title="懒加载服务"></a>懒加载服务</h2><p><code>BaseService</code> 提供 <code>LazyGetRequiredService&lt;T&gt;()</code> 方法，用于在需要时获取服务，避免构造函数注入过多依赖：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MyService</span> : <span class="title">BaseService</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">DoSomething</span>()</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 懒加载获取服务</span></span><br><span class="line">        <span class="keyword">var</span> emailService = LazyGetRequiredService&lt;IEmailService&gt;();</span><br><span class="line">        <span class="keyword">await</span> emailService.SendAsync(...);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>#中台 #中台&#x2F;DI生命周期 #中台&#x2F;特性注解  #Code&#x2F;C#</p>]]>
    </content>
    <id>https://zizai.cc/posts/di-and-repository/</id>
    <link href="https://zizai.cc/posts/di-and-repository/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>命名空间：ZhonTai.Admin.Services / ZhonTai.Admin.Core.Repositories / ZhonTai.Admin.Core.RegisterModules</summary>
    <title>依赖注入与仓储</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="CAP" scheme="https://zizai.cc/tags/CAP/"/>
    <content>
      <![CDATA[<p>CAP 是 .NET 生态中的轻量级分布式事务与事件驱动解决方案，提供可靠的事务性消息传递机制，支持跨服务的发布&#x2F;订阅模式，无缝集成 Kafka、RabbitMQ 等主流消息队列。CAP 确保分布式系统在异步通信场景下实现最终一致性，有效简化微服务架构中复杂事务的协调与管理工作。</p><blockquote><p>更多信息请参阅 <a href="https://cap.dotnetcore.xyz/">CAP 官方文档</a>。<br>若尚未安装 RabbitMQ，建议先完成 <a href="https://www.rabbitmq.com/download.html">RabbitMQ 安装部署</a> 再进行后续开发。</p></blockquote><hr><h2 id="一、环境配置"><a href="#一、环境配置" class="headerlink" title="一、环境配置"></a>一、环境配置</h2><h3 id="1-1-NuGet-包依赖"><a href="#1-1-NuGet-包依赖" class="headerlink" title="1.1 NuGet 包依赖"></a>1.1 NuGet 包依赖</h3><p>在 <code>Host.csproj</code> 中按需配置 CAP 的存储（SQL Server）和消息队列（RabbitMQ）：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- CAP 核心包 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">&quot;DotNetCore.CAP.SqlServer&quot;</span> <span class="attr">Version</span>=<span class="string">&quot;8.3.2&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">&quot;DotNetCore.CAP.RabbitMQ&quot;</span> <span class="attr">Version</span>=<span class="string">&quot;8.3.2&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">&quot;DotNetCore.CAP.Dashboard&quot;</span> <span class="attr">Version</span>=<span class="string">&quot;8.3.2&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 数据库驱动 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">&quot;FreeSql.Provider.SqlServer&quot;</span> <span class="attr">Version</span>=<span class="string">&quot;3.5.104&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="1-2-全局配置（Program-cs）"><a href="#1-2-全局配置（Program-cs）" class="headerlink" title="1.2 全局配置（Program.cs）"></a>1.2 全局配置（Program.cs）</h3><p>在 <code>Program.cs</code> 中初始化 CAP 并注入相关依赖：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> HostApp(<span class="keyword">new</span> HostAppOptions</span><br><span class="line">&#123;</span><br><span class="line">    ConfigurePostServices = context =&gt;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 加载应用配置和程序集</span></span><br><span class="line">        <span class="keyword">var</span> appConfig = AppInfo.GetRequiredService&lt;AppConfig&gt;(<span class="literal">false</span>);</span><br><span class="line">        <span class="keyword">var</span> assemblies = AssemblyHelper.GetAssemblyList(appConfig.AssemblyNames);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取数据库和 RabbitMQ 配置</span></span><br><span class="line">        <span class="keyword">var</span> dbConfig = AppInfo.GetRequiredService&lt;DbConfig&gt;(<span class="literal">false</span>);</span><br><span class="line">        <span class="keyword">var</span> rabbitMQ = context.Configuration.GetSection(<span class="string">&quot;CAP:RabbitMq&quot;</span>).Get&lt;RabbitMQOptions&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 配置 CAP</span></span><br><span class="line">        context.Services.AddCap(config =&gt;</span><br><span class="line">        &#123;</span><br><span class="line">            config.Version = <span class="string">&quot;v1&quot;</span>; <span class="comment">// 环境隔离标识</span></span><br><span class="line">            config.UseSqlServer(dbConfig.ConnectionString); <span class="comment">// SQL Server 存储</span></span><br><span class="line">            config.UseRabbitMQ(mqConfig =&gt; <span class="comment">// RabbitMQ 传输</span></span><br><span class="line">            &#123;</span><br><span class="line">                mqConfig.HostName = rabbitMQ.HostName;</span><br><span class="line">                mqConfig.Port = rabbitMQ.Port;</span><br><span class="line">                mqConfig.UserName = rabbitMQ.UserName;</span><br><span class="line">                mqConfig.Password = rabbitMQ.Password;</span><br><span class="line">                mqConfig.ExchangeName = rabbitMQ.ExchangeName;</span><br><span class="line">            &#125;);</span><br><span class="line">            config.UseDashboard(); <span class="comment">// 启用监控面板</span></span><br><span class="line">        &#125;).AddSubscriberAssembly(assemblies); <span class="comment">// 自动注册订阅者</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;).Run(<span class="keyword">args</span>, <span class="keyword">typeof</span>(Program).Assembly);</span><br></pre></td></tr></table></figure><hr><h2 id="二、定义订阅与事件"><a href="#二、定义订阅与事件" class="headerlink" title="二、定义订阅与事件"></a>二、定义订阅与事件</h2><h3 id="2-1-订阅命名（契约层）"><a href="#2-1-订阅命名（契约层）" class="headerlink" title="2.1 订阅命名（契约层）"></a>2.1 订阅命名（契约层）</h3><p>在 <code>Api.Contracts</code> 项目的 <code>Core/Consts</code> 目录下定义订阅名常量：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 订阅命名常量（确保全局唯一）</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">SubscribeNames</span></span><br><span class="line">&#123;</span><br><span class="line">    [<span class="meta">Description(<span class="string">&quot;模块操作事件&quot;</span>)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">const</span> <span class="built_in">string</span> ModuleAdd = <span class="string">&quot;zhontai.admin.module.add&quot;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-2-事件类（契约层）"><a href="#2-2-事件类（契约层）" class="headerlink" title="2.2 事件类（契约层）"></a>2.2 事件类（契约层）</h3><p>在 <code>Api.Contracts</code> 项目的 <code>Services/Module/Events</code> 目录下定义事件类：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块新增事件（约定以 Event 作为后缀名，需支持序列化）</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleAddEvent</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">long</span> ModuleId &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> ModuleName &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="comment">// 其他业务字段...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="三、发布消息"><a href="#三、发布消息" class="headerlink" title="三、发布消息"></a>三、发布消息</h2><p>通过 <code>ICapPublisher</code> 在服务接口中发送消息：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Order(1010)</span>]</span><br><span class="line">[<span class="meta">DynamicApi(Area = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> AppRepositoryBase&lt;ModuleEntity&gt; _moduleRep;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> ICapPublisher _capPublisher;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params"></span></span></span><br><span class="line"><span class="params"><span class="function">        AppRepositoryBase&lt;ModuleEntity&gt; moduleRep,</span></span></span><br><span class="line"><span class="params"><span class="function">        ICapPublisher capPublisher</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleRep = moduleRep;</span><br><span class="line">        _capPublisher = capPublisher;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 新增模块并发布事件</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;<span class="built_in">long</span>&gt; <span class="title">AddAsync</span>(<span class="params">ModuleAddInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> entity = Mapper.Map&lt;ModuleEntity&gt;(input);</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.InsertAsync(entity);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 发布 CAP 消息</span></span><br><span class="line">        <span class="keyword">var</span> moduleAddEvent = input.Adapt&lt;ModuleAddEvent&gt;();</span><br><span class="line">        <span class="keyword">await</span> _capPublisher.PublishAsync(</span><br><span class="line">            SubscribeNames.ModuleAdd,</span><br><span class="line">            moduleAddEvent</span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> entity.Id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="四、订阅消息"><a href="#四、订阅消息" class="headerlink" title="四、订阅消息"></a>四、订阅消息</h2><p>创建订阅服务类，继承 <code>ICapSubscribe</code> 并标记订阅方法：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleSubscribeService</span> : <span class="title">ICapSubscribe</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 处理模块新增事件</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">NonAction</span>] <span class="comment">// 防止被暴露为 API 端点</span></span><br><span class="line">    [<span class="meta">CapSubscribe(SubscribeNames.ModuleAdd)</span>]</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">HandleModuleAddAsync</span>(<span class="params">ModuleAddEvent @<span class="keyword">event</span></span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 业务逻辑：发送通知、更新缓存等</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="关键规则"><a href="#关键规则" class="headerlink" title="关键规则"></a>关键规则</h3><table><thead><tr><th>规则</th><th>说明</th></tr></thead><tbody><tr><td>接口继承</td><td>必须实现<code>ICapSubscribe</code> 接口</td></tr><tr><td>特性标记</td><td>订阅方法需标注<code>[CapSubscribe(&quot;事件名&quot;)]</code></td></tr><tr><td>参数匹配</td><td>事件参数类型需与发布时保持一致</td></tr></tbody></table><hr><h2 id="五、监控与调试"><a href="#五、监控与调试" class="headerlink" title="五、监控与调试"></a>五、监控与调试</h2><h3 id="5-1-CAP-Dashboard"><a href="#5-1-CAP-Dashboard" class="headerlink" title="5.1 CAP Dashboard"></a>5.1 CAP Dashboard</h3><p>通过 <code>http://your-domain/cap</code> 访问监控面板，可查看消息状态、重试记录等关键信息。</p><h3 id="5-2-日志分析"><a href="#5-2-日志分析" class="headerlink" title="5.2 日志分析"></a>5.2 日志分析</h3><ul><li><strong>成功订阅</strong>：检查日志确认事件是否被正常消费</li><li><strong>失败处理</strong>：CAP 自动重试（默认 50 次），可在 Dashboard 中监控处理进度</li></ul><hr><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ol><li><strong>版本隔离</strong>：通过 <code>config.Version</code> 区分不同环境的消息，避免环境间消息串扰</li><li><strong>配置检查</strong>：确保 RabbitMQ 和 SQL Server 连接字符串配置正确</li><li><strong>事件序列化</strong>：事件类需支持序列化，建议使用简单数据结构</li></ol><p>#消息队列 #RabbitMQ #分布式 #分布式&#x2F;消息传递 #中台&#x2F;分布式微服务</p>]]>
    </content>
    <id>https://zizai.cc/posts/cap-event-bus/</id>
    <link href="https://zizai.cc/posts/cap-event-bus/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>CAP 是 .NET 生态中的轻量级分布式事务与事件驱动解决方案，提供可靠的事务性消息传递机制，支持跨服务的发布/订阅模式，无缝集成 Kafka、RabbitMQ 等主流消息队列。CAP 确保分布式系统在异步通信场景下实现最终一致性，有效简</summary>
    <title>CAP 事件总线</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <content>
      <![CDATA[<p>命名空间：<code>ZhonTai.Common.Helpers</code></p><p>提供多种加密算法的帮助类，涵盖 MD5、DES 以及国密 SM2&#x2F;SM3&#x2F;SM4 算法。</p><hr><h2 id="MD5-加密"><a href="#MD5-加密" class="headerlink" title="MD5 加密"></a>MD5 加密</h2><p>类名：<code>MD5Encrypt</code></p><h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>密码哈希存储</li><li>文件完整性校验</li><li>数据指纹生成</li></ul><h3 id="使用示例"><a href="#使用示例" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 16位MD5</span></span><br><span class="line"><span class="keyword">var</span> hash16 = MD5Encrypt.Encrypt16(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 32位MD5（默认大写）</span></span><br><span class="line"><span class="keyword">var</span> hash32 = MD5Encrypt.Encrypt32(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 32位MD5（小写）</span></span><br><span class="line"><span class="keyword">var</span> hash32Lower = MD5Encrypt.Encrypt32(<span class="string">&quot;hello&quot;</span>, lowerCase: <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 64位MD5（Base64格式）</span></span><br><span class="line"><span class="keyword">var</span> hash64 = MD5Encrypt.Encrypt64(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 计算文件流的MD5</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">var</span> stream = File.OpenRead(<span class="string">&quot;test.txt&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> fileHash = MD5Encrypt.GetHash(stream);</span><br></pre></td></tr></table></figure><hr><h2 id="DES-加解密"><a href="#DES-加解密" class="headerlink" title="DES 加解密"></a>DES 加解密</h2><p>类名：<code>DesEncrypt</code></p><p>采用 <strong>ECB 模式 + PKCS7 填充</strong></p><h3 id="应用场景-1"><a href="#应用场景-1" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>敏感配置信息加解密</li><li>数据传输加密（非高安全场景）</li></ul><h3 id="使用示例-1"><a href="#使用示例-1" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> key = <span class="string">&quot;mysecretkey&quot;</span>; <span class="comment">// 密钥长度需 &gt;= 8位</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Base64 格式加解密</span></span><br><span class="line"><span class="keyword">var</span> encrypted = DesEncrypt.Encrypt(<span class="string">&quot;hello&quot;</span>, key);</span><br><span class="line"><span class="keyword">var</span> decrypted = DesEncrypt.Decrypt(encrypted, key);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 16进制格式加解密</span></span><br><span class="line"><span class="keyword">var</span> encryptedHex = DesEncrypt.Encrypt4Hex(<span class="string">&quot;hello&quot;</span>, key);</span><br><span class="line"><span class="keyword">var</span> decryptedHex = DesEncrypt.Decrypt4Hex(encryptedHex, key);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用默认密钥（不推荐生产使用）</span></span><br><span class="line"><span class="keyword">var</span> encrypted2 = DesEncrypt.Encrypt(<span class="string">&quot;hello&quot;</span>);</span><br></pre></td></tr></table></figure><blockquote><p><strong>注意</strong>：DES 算法安全性较低，建议仅用于非敏感场景。高安全场景请使用 SM4。</p></blockquote><hr><h2 id="国密-SM2-非对称加密"><a href="#国密-SM2-非对称加密" class="headerlink" title="国密 SM2 非对称加密"></a>国密 SM2 非对称加密</h2><p>类名：<code>SM2Encryption</code>，基于 BouncyCastle</p><h3 id="应用场景-2"><a href="#应用场景-2" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>数字签名与验签</li><li>密钥交换</li><li>敏感数据加密传输</li></ul><h3 id="使用示例-2"><a href="#使用示例-2" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成SM2密钥对</span></span><br><span class="line">SM2Encryption.GenerateSM2KeyPair(<span class="keyword">out</span> <span class="built_in">string</span> privateKey, <span class="keyword">out</span> <span class="built_in">string</span> publicKey);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 公钥加密</span></span><br><span class="line"><span class="keyword">var</span> encrypted = SM2Encryption.Encrypt(<span class="string">&quot;敏感数据&quot;</span>, publicKey);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 私钥解密</span></span><br><span class="line"><span class="keyword">var</span> decrypted = SM2Encryption.Decrypt(encrypted, privateKey);</span><br></pre></td></tr></table></figure><hr><h2 id="国密-SM3-哈希算法"><a href="#国密-SM3-哈希算法" class="headerlink" title="国密 SM3 哈希算法"></a>国密 SM3 哈希算法</h2><p>类名：<code>SM3Encryption</code>，基于 BouncyCastle</p><h3 id="应用场景-3"><a href="#应用场景-3" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>密码哈希存储</li><li>数据完整性校验</li><li>数字签名</li></ul><h3 id="使用示例-3"><a href="#使用示例-3" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// SM3 哈希</span></span><br><span class="line"><span class="keyword">var</span> hash = SM3Encryption.ComputeSM3Hash(<span class="string">&quot;hello&quot;</span>);             <span class="comment">// byte[]</span></span><br><span class="line"><span class="keyword">var</span> hashHex = SM3Encryption.ComputeSM3HashHex(<span class="string">&quot;hello&quot;</span>);        <span class="comment">// 16进制字符串</span></span><br><span class="line"><span class="keyword">var</span> hashBase64 = SM3Encryption.ComputeSM3HashBase64(<span class="string">&quot;hello&quot;</span>);  <span class="comment">// Base64字符串</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// HMAC-SM3（带密钥的哈希）</span></span><br><span class="line"><span class="keyword">var</span> hmac = SM3Encryption.ComputeHMACSM3Hex(<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;secretKey&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> hmacBase64 = SM3Encryption.ComputeHMACSM3Base64(<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;secretKey&quot;</span>);</span><br></pre></td></tr></table></figure><hr><h2 id="国密-SM4-对称加密"><a href="#国密-SM4-对称加密" class="headerlink" title="国密 SM4 对称加密"></a>国密 SM4 对称加密</h2><p>类名：<code>SM4Encryption</code>，支持 <strong>ECB</strong> 和 <strong>CBC</strong> 模式，基于 BouncyCastle</p><h3 id="应用场景-4"><a href="#应用场景-4" class="headerlink" title="应用场景"></a>应用场景</h3><ul><li>数据库敏感字段加密</li><li>接口数据加密传输</li><li>配置文件敏感信息加密</li></ul><h3 id="使用示例-4"><a href="#使用示例-4" class="headerlink" title="使用示例"></a>使用示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> key = <span class="string">&quot;1234567890abcdef&quot;</span>; <span class="comment">// 16字节密钥</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ECB 模式</span></span><br><span class="line"><span class="keyword">var</span> encrypted = SM4Encryption.Encrypt(<span class="string">&quot;hello&quot;</span>, key, iv: <span class="literal">null</span>, mode: <span class="string">&quot;ECB&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> decrypted = SM4Encryption.Decrypt(encrypted, key, iv: <span class="literal">null</span>, mode: <span class="string">&quot;ECB&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// CBC 模式</span></span><br><span class="line"><span class="keyword">var</span> iv = <span class="string">&quot;1234567890abcdef&quot;</span>;  <span class="comment">// 16字节IV</span></span><br><span class="line"><span class="keyword">var</span> encryptedCbc = SM4Encryption.Encrypt(<span class="string">&quot;hello&quot;</span>, key, iv, mode: <span class="string">&quot;CBC&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> decryptedCbc = SM4Encryption.Decrypt(encryptedCbc, key, iv, mode: <span class="string">&quot;CBC&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用16进制格式</span></span><br><span class="line"><span class="keyword">var</span> encryptedHex = SM4Encryption.Encrypt(<span class="string">&quot;hello&quot;</span>, key, iv: <span class="literal">null</span>, mode: <span class="string">&quot;ECB&quot;</span>, isHex: <span class="literal">true</span>);</span><br><span class="line"><span class="keyword">var</span> decryptedHex = SM4Encryption.Decrypt(encryptedHex, key, iv: <span class="literal">null</span>, mode: <span class="string">&quot;ECB&quot;</span>, isHex: <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 byte[] 密钥（更安全）</span></span><br><span class="line"><span class="keyword">var</span> keyBytes = System.Text.Encoding.UTF8.GetBytes(<span class="string">&quot;1234567890abcdef&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> encrypted2 = SM4Encryption.Encrypt(<span class="string">&quot;hello&quot;</span>, keyBytes, iv: <span class="literal">null</span>);</span><br></pre></td></tr></table></figure><h3 id="输出格式"><a href="#输出格式" class="headerlink" title="输出格式"></a>输出格式</h3><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>isHex: false</code>（默认）</td><td>输出 Base64 格式密文</td></tr><tr><td><code>isHex: true</code></td><td>输出 16 进制格式密文</td></tr></tbody></table><p>#中台 #中台&#x2F;公共帮助类 #加密 #加密&#x2F;哈希算法 #加密&#x2F;对称加密</p>]]>
    </content>
    <id>https://zizai.cc/posts/encryption-helper/</id>
    <link href="https://zizai.cc/posts/encryption-helper/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>命名空间：ZhonTai.Common.Helpers</summary>
    <title>加密帮助类</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <content>
      <![CDATA[<h2 id="一、开发界面访问空白"><a href="#一、开发界面访问空白" class="headerlink" title="一、开发界面访问空白"></a>一、开发界面访问空白</h2><p>当访问某个界面出现空白时，请按以下步骤排查：</p><ol><li>打开该页面对应的视图文件（<code>.vue</code> 文件）</li><li>检查 <code>&lt;template&gt;</code> 节点内的代码结构</li><li><strong>确保模板中只有一个根节点</strong>，多余的同级节点必须合并到根节点内部</li></ol><blockquote><p>⚠️ 注意：注释也是一个标签节点，会影响模板结构的判断。</p></blockquote><p><strong>错误示例 ×</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;!-- 注释也是一个标签节点 --&gt;</span><br><span class="line">  &lt;div&gt;&lt;/div&gt;</span><br><span class="line">  &lt;div&gt;&lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br></pre></td></tr></table></figure><p><strong>正确示例 √</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;div&gt;</span><br><span class="line">    &lt;!-- 注释也是一个标签节点 --&gt;</span><br><span class="line">    &lt;div&gt;&lt;/div&gt;</span><br><span class="line">    &lt;div&gt;&lt;/div&gt;</span><br><span class="line">  &lt;/div&gt;</span><br><span class="line">&lt;/template&gt;</span><br></pre></td></tr></table></figure><hr><h2 id="二、pnpm-安装-npm-包不成功"><a href="#二、pnpm-安装-npm-包不成功" class="headerlink" title="二、pnpm 安装 npm 包不成功"></a>二、pnpm 安装 npm 包不成功</h2><h3 id="问题原因"><a href="#问题原因" class="headerlink" title="问题原因"></a>问题原因</h3><p>由于网络问题或地理位置限制，直接从 npm 官方仓库下载包可能速度极慢或无法访问。</p><h3 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h3><p>使用 <code>nrm</code> 工具管理 npm 镜像源，无需手动编辑配置文件。</p><h4 id="1-安装-nrm（以管理员身份运行-CMD）"><a href="#1-安装-nrm（以管理员身份运行-CMD）" class="headerlink" title="1. 安装 nrm（以管理员身份运行 CMD）"></a>1. 安装 nrm（以管理员身份运行 CMD）</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g nrm --registry https://registry.npmmirror.com</span><br></pre></td></tr></table></figure><h4 id="2-列出所有可用的镜像源"><a href="#2-列出所有可用的镜像源" class="headerlink" title="2. 列出所有可用的镜像源"></a>2. 列出所有可用的镜像源</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nrm <span class="built_in">ls</span></span><br></pre></td></tr></table></figure><p>显示结果如下（<code>*</code> 表示当前使用的源）：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">npm ---------- https://registry.npmjs.org/</span><br><span class="line">yarn --------- https://registry.yarnpkg.com/</span><br><span class="line">tencent ------ https://mirrors.cloud.tencent.com/npm/</span><br><span class="line">cnpm --------- https://r.cnpmjs.org/</span><br><span class="line">* taobao ------- https://registry.npmmirror.com/</span><br><span class="line">npmMirror ---- https://skimdb.npmjs.com/registry/</span><br></pre></td></tr></table></figure><h4 id="3-切换到淘宝镜像源"><a href="#3-切换到淘宝镜像源" class="headerlink" title="3. 切换到淘宝镜像源"></a>3. 切换到淘宝镜像源</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nrm use taobao</span><br></pre></td></tr></table></figure><hr><h2 id="三、页面访问正常，刷新后-404-错误"><a href="#三、页面访问正常，刷新后-404-错误" class="headerlink" title="三、页面访问正常，刷新后 404 错误"></a>三、页面访问正常，刷新后 404 错误</h2><h3 id="问题原因-1"><a href="#问题原因-1" class="headerlink" title="问题原因"></a>问题原因</h3><p>单页应用（SPA）的路由由客户端 JavaScript 处理，刷新页面时浏览器向服务器请求当前 URL，但服务器上并不存在该路径对应的文件。</p><h3 id="解决方案-1"><a href="#解决方案-1" class="headerlink" title="解决方案"></a>解决方案</h3><p>在服务器上添加回退路由，将所有不匹配静态资源的请求重定向到 <code>index.html</code>。</p><h4 id="Nginx-配置"><a href="#Nginx-配置" class="headerlink" title="Nginx 配置"></a>Nginx 配置</h4><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">location</span> / &#123;</span><br><span class="line">  <span class="attribute">try_files</span> <span class="variable">$uri</span> <span class="variable">$uri</span>/ /index.html;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="IIS-配置"><a href="#IIS-配置" class="headerlink" title="IIS 配置"></a>IIS 配置</h4><ol><li>安装 <strong>IIS UrlRewrite</strong> 扩展</li><li>在网站根目录下创建 <code>web.config</code> 文件：</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">system.webServer</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">rewrite</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">rules</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">rule</span> <span class="attr">name</span>=<span class="string">&quot;Handle History Mode and custom 404/500&quot;</span> <span class="attr">stopProcessing</span>=<span class="string">&quot;true&quot;</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">match</span> <span class="attr">url</span>=<span class="string">&quot;(.*)&quot;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">conditions</span> <span class="attr">logicalGrouping</span>=<span class="string">&quot;MatchAll&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">add</span> <span class="attr">input</span>=<span class="string">&quot;&#123;REQUEST_FILENAME&#125;&quot;</span> <span class="attr">matchType</span>=<span class="string">&quot;IsFile&quot;</span> <span class="attr">negate</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">add</span> <span class="attr">input</span>=<span class="string">&quot;&#123;REQUEST_FILENAME&#125;&quot;</span> <span class="attr">matchType</span>=<span class="string">&quot;IsDirectory&quot;</span> <span class="attr">negate</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line">          <span class="tag">&lt;/<span class="name">conditions</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">action</span> <span class="attr">type</span>=<span class="string">&quot;Rewrite&quot;</span> <span class="attr">url</span>=<span class="string">&quot;/&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">rule</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">rules</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">rewrite</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">system.webServer</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure><blockquote><p>💡 参考来源：<a href="https://router.vuejs.org/zh/guide/essentials/history-mode.html">Vue Router - 不同的历史记录模式</a></p></blockquote><p>#中台&#x2F;常见问题 #中台&#x2F;配置文件 #中台&#x2F;搭建项目框架 #Windows #中台&#x2F;搭建网关</p>]]>
    </content>
    <id>https://zizai.cc/posts/frontend-faq-solutions/</id>
    <link href="https://zizai.cc/posts/frontend-faq-solutions/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>当访问某个界面出现空白时，请按以下步骤排查：</summary>
    <title>前端常见问题解决方案</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="国际化" scheme="https://zizai.cc/tags/%E5%9B%BD%E9%99%85%E5%8C%96/"/>
    <content>
      <![CDATA[<p>项目基于 vue-i18n 实现多语言支持，默认支持中文、繁体中文、英文三种语言切换，并与 Element Plus 语言包无缝集成。</p><blockquote><p>更多用法请参阅 <a href="https://vue-i18n.intlify.dev/">Vue I18n 官方文档</a>。</p></blockquote><h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">src/i18n/</span><br><span class="line">├─ index.ts                      # i18n 实例配置与导出</span><br><span class="line">├─ admin/                        # 业务模块翻译文件</span><br><span class="line">│  ├─ en.ts                      # 英文翻译</span><br><span class="line">│  ├─ zh-cn.ts                   # 简体中文</span><br><span class="line">│  └─ zh-tw.ts                   # 繁体中文</span><br><span class="line">└─ admin/enum/                   # 枚举翻译文件</span><br><span class="line">   ├─ en.ts</span><br><span class="line">   ├─ zh-cn.ts</span><br><span class="line">   └─ zh-tw.ts</span><br></pre></td></tr></table></figure><p>翻译文件按语言代码命名（如 <code>en</code>、<code>zh-cn</code>、<code>zh-tw</code>），统一导出默认对象。所有文件通过 <code>import.meta.glob</code> 自动收集合并。</p><h2 id="翻译文件格式"><a href="#翻译文件格式" class="headerlink" title="翻译文件格式"></a>翻译文件格式</h2><p>推荐以中文原文作为 Key，值对应目标语言翻译。支持参数插值及特殊语法转义。</p><h3 id="基本结构"><a href="#基本结构" class="headerlink" title="基本结构"></a>基本结构</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/i18n/admin/en.ts</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  <span class="string">&#x27;账号&#x27;</span>: <span class="string">&#x27;Account&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;手机号&#x27;</span>: <span class="string">&#x27;Mobile&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;确定要删除【&#123;name&#125;】?&#x27;</span>: <span class="string">&#x27;Are you sure you want to delete [&#123;name&#125;]?&#x27;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="参数格式化"><a href="#参数格式化" class="headerlink" title="参数格式化"></a>参数格式化</h3><p>使用 <code>{0}</code>、<code>{1}</code> 或命名参数 <code>{name}</code> 传递动态内容。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 翻译定义</span></span><br><span class="line"><span class="string">&#x27;&#123;0&#125;秒前&#x27;</span>: <span class="string">&#x27;&#123;0&#125; seconds ago&#x27;</span></span><br><span class="line"><span class="string">&#x27;确定要&#123;action&#125;【&#123;name&#125;】?&#x27;</span>: <span class="string">&#x27;Are you sure you want to &#123;action&#125; [&#123;name&#125;]?&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="title function_">t</span>(<span class="string">&#x27;&#123;0&#125;秒前&#x27;</span>, [<span class="number">5</span>])                                   <span class="comment">// &quot;5 seconds ago&quot;</span></span><br><span class="line"><span class="title function_">t</span>(<span class="string">&#x27;确定要&#123;action&#125;【&#123;name&#125;】?&#x27;</span>, &#123; <span class="attr">action</span>: <span class="title function_">t</span>(<span class="string">&#x27;删除&#x27;</span>), <span class="attr">name</span>: <span class="string">&#x27;张三&#x27;</span> &#125;)</span><br></pre></td></tr></table></figure><h3 id="特殊字符转义"><a href="#特殊字符转义" class="headerlink" title="特殊字符转义"></a>特殊字符转义</h3><p>若 Key 中包含模板语法保留字符（如 <code>@</code>、<code>{</code>），需使用 <code>\</code> 转义。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&#x27;代码区块：\\@\\&#123; //C#代码 \\&#125;&#x27;</span>: <span class="string">&#x27;Code block: \\@\\&#123; //C# code \\&#125;&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;模型名称：\\@(gen.Model.Name)&#x27;</span>: <span class="string">&#x27;Model Name: \\@(gen.Model.Name)&#x27;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="换行"><a href="#换行" class="headerlink" title="换行"></a>换行</h3><p>直接使用 <code>\n</code> 表示换行符。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&#x27;一、请按照模板格式准备数据\n二、选择重复数据处理方式&#x27;</span>: <span class="string">&#x27;1. Prepare data per template\n2. Choose duplicate handling&#x27;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="枚举翻译"><a href="#枚举翻译" class="headerlink" title="枚举翻译"></a>枚举翻译</h2><p>枚举值翻译独立存放于 <code>src/i18n/admin/enum/</code> 目录，仅含一级 Key。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/i18n/admin/enum/en.ts</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  <span class="string">&#x27;男&#x27;</span>: <span class="string">&#x27;Male&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;女&#x27;</span>: <span class="string">&#x27;Female&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;启用&#x27;</span>: <span class="string">&#x27;Enabled&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;禁用&#x27;</span>: <span class="string">&#x27;Disabled&#x27;</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在组件中通过 <code>t</code> 函数直接使用。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;el-tag&gt;&#123;&#123; t(&#x27;启用&#x27;) &#125;&#125;&lt;/el-tag&gt;</span><br><span class="line">&lt;/template&gt;</span><br></pre></td></tr></table></figure><h2 id="在组件中使用"><a href="#在组件中使用" class="headerlink" title="在组件中使用"></a>在组件中使用</h2><h3 id="模板中"><a href="#模板中" class="headerlink" title="模板中"></a>模板中</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;!-- 普通文本 --&gt;</span><br><span class="line">  &lt;span&gt;&#123;&#123; t(&#x27;账号&#x27;) &#125;&#125;&lt;/span&gt;</span><br><span class="line"></span><br><span class="line">  &lt;!-- 带参数 --&gt;</span><br><span class="line">  &lt;p&gt;&#123;&#123; t(&#x27;确定要删除【&#123;name&#125;】?&#x27;, &#123; name: row.name &#125;) &#125;&#125;&lt;/p&gt;</span><br><span class="line">&lt;/template&gt;</span><br></pre></td></tr></table></figure><h3 id="Script-中"><a href="#Script-中" class="headerlink" title="Script 中"></a>Script 中</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; t &#125; <span class="keyword">from</span> <span class="string">&#x27;/@/i18n&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 直接调用</span></span><br><span class="line"><span class="keyword">const</span> message = <span class="title function_">t</span>(<span class="string">&#x27;操作成功&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 带参数</span></span><br><span class="line">proxy.<span class="property">$modal</span>.<span class="title function_">confirm</span>(<span class="title function_">t</span>(<span class="string">&#x27;确定要&#123;action&#125;【&#123;name&#125;】?&#x27;</span>, &#123; <span class="attr">action</span>: <span class="title function_">t</span>(<span class="string">&#x27;删除&#x27;</span>), <span class="attr">name</span>: userName &#125;))</span><br></pre></td></tr></table></figure><h2 id="与-Element-Plus-集成"><a href="#与-Element-Plus-集成" class="headerlink" title="与 Element Plus 集成"></a>与 Element Plus 集成</h2><p>项目已自动将 Element Plus 语言包合并至 i18n 实例，切换语言时组件内部文案同步更新。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.ts 已做合并</span></span><br><span class="line">messages[locale] = &#123;</span><br><span class="line">  <span class="attr">el</span>: elementLocales[locale].<span class="property">el</span>,           <span class="comment">// Element Plus 语言配置</span></span><br><span class="line">  ...<span class="title function_">mergeObjects</span>(fragments),              <span class="comment">// 自定义翻译</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="配置与实例"><a href="#配置与实例" class="headerlink" title="配置与实例"></a>配置与实例</h2><p><code>src/i18n/index.ts</code> 负责创建并导出 i18n 实例，同时提供以下便捷方法：</p><table><thead><tr><th>导出项</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>i18n</code></td><td><code>I18n</code></td><td>Vue I18n 实例，用于全局注册或插件配置</td></tr><tr><td><code>t</code></td><td><code>function</code></td><td>翻译函数，等同于<code>i18n.global.t</code>，支持参数插值</td></tr><tr><td><code>locale</code></td><td><code>WritableComputedRef&lt;string&gt;</code></td><td>响应式当前语言，修改后全局生效</td></tr><tr><td><code>lang</code></td><td><code>ComputedRef&lt;string&gt;</code></td><td>后端接口所需语言格式（<code>zh-CN</code> | <code>zh-TW</code> | <code>en</code>）</td></tr></tbody></table><h2 id="切换语言"><a href="#切换语言" class="headerlink" title="切换语言"></a>切换语言</h2><p>通过修改 <code>locale.value</code> 并更新 <code>themeConfig.globalI18n</code> 实现持久化。通常在页面右上角提供下拉菜单供用户切换。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;el-dropdown trigger=&quot;click&quot; @command=&quot;onLanguageChange&quot;&gt;</span><br><span class="line">    &lt;div class=&quot;lang-icon&quot;&gt;</span><br><span class="line">      &lt;i class=&quot;iconfont icon-diqiu&quot; :title=&quot;t(&#x27;语言切换&#x27;)&quot;&gt;&lt;/i&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">    &lt;template #dropdown&gt;</span><br><span class="line">      &lt;el-dropdown-menu&gt;</span><br><span class="line">        &lt;el-dropdown-item command=&quot;zh-cn&quot; :disabled=&quot;currentLang === &#x27;zh-cn&#x27;&quot;&gt;</span><br><span class="line">          &#123;&#123; t(&#x27;简体中文&#x27;) &#125;&#125;</span><br><span class="line">        &lt;/el-dropdown-item&gt;</span><br><span class="line">        &lt;el-dropdown-item command=&quot;en&quot; :disabled=&quot;currentLang === &#x27;en&#x27;&quot;&gt;</span><br><span class="line">          English</span><br><span class="line">        &lt;/el-dropdown-item&gt;</span><br><span class="line">        &lt;el-dropdown-item command=&quot;zh-tw&quot; :disabled=&quot;currentLang === &#x27;zh-tw&#x27;&quot;&gt;</span><br><span class="line">          &#123;&#123; t(&#x27;繁体中文&#x27;) &#125;&#125;</span><br><span class="line">        &lt;/el-dropdown-item&gt;</span><br><span class="line">      &lt;/el-dropdown-menu&gt;</span><br><span class="line">    &lt;/template&gt;</span><br><span class="line">  &lt;/el-dropdown&gt;</span><br><span class="line">&lt;/template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script setup lang=&quot;ts&quot;&gt;</span><br><span class="line">import &#123; useThemeConfig &#125; from &#x27;/@/stores/themeConfig&#x27;</span><br><span class="line">import &#123; t, locale &#125; from &#x27;/@/i18n&#x27;</span><br><span class="line">import &#123; Local &#125; from &#x27;/@/utils/storage&#x27;</span><br><span class="line">import other from &#x27;/@/utils/other&#x27;</span><br><span class="line"></span><br><span class="line">const storesThemeConfig = useThemeConfig()</span><br><span class="line">const &#123; themeConfig &#125; = storeToRefs(storesThemeConfig)</span><br><span class="line"></span><br><span class="line">// 当前激活的语言</span><br><span class="line">const currentLang = computed(() =&gt; themeConfig.value.globalI18n)</span><br><span class="line"></span><br><span class="line">// 语言切换</span><br><span class="line">const onLanguageChange = (lang: string) =&gt; &#123;</span><br><span class="line">  Local.remove(&#x27;themeConfig&#x27;)</span><br><span class="line">  themeConfig.value.globalI18n = lang</span><br><span class="line">  Local.set(&#x27;themeConfig&#x27;, themeConfig.value)</span><br><span class="line">  locale.value = lang</span><br><span class="line">  other.useTitle() // 更新页面标题语言</span><br><span class="line">&#125;</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><p>切换后，所有使用 <code>t()</code> 的文本及 Element Plus 组件内部文案均会实时更新。</p><h2 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h2><ul><li><strong>Key 命名</strong>：始终以中文原文作为 Key，便于阅读与维护。</li><li><strong>参数顺序</strong>：优先使用命名参数 <code>{name}</code>，提高可读性。</li><li><strong>枚举值</strong>：统一放入 <code>enum</code> 目录，避免与业务文本混淆。</li><li><strong>缺失处理</strong>：当前配置未设置 <code>missing</code> 回调，缺失时回退至 Key 本身（即显示中文原文）。</li></ul><p>#前端 #前端&#x2F;Vue #前端&#x2F;Vue&#x2F;国际化 #配置文件 #TypeScript</p>]]>
    </content>
    <id>https://zizai.cc/posts/frontend-i18n/</id>
    <link href="https://zizai.cc/posts/frontend-i18n/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>项目基于 vue-i18n 实现多语言支持，默认支持中文、繁体中文、英文三种语言切换，并与 Element Plus 语言包无缝集成。</summary>
    <title>前端国际化</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="gRPC" scheme="https://zizai.cc/tags/gRPC/"/>
    <content>
      <![CDATA[<p>本文档为开发者提供标准化的 gRPC 接口开发指南，涵盖从协议定义、服务实现到测试验证的全流程规范。适用于 .NET 技术栈的微服务场景，确保跨服务通信的高效性、兼容性和可维护性。</p><hr><h2 id="1-系统内置类型说明"><a href="#1-系统内置类型说明" class="headerlink" title="1. 系统内置类型说明"></a>1. 系统内置类型说明</h2><h3 id="1-1-基础类型示例"><a href="#1-1-基础类型示例" class="headerlink" title="1.1 基础类型示例"></a>1.1 基础类型示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> Proto 长整型（解决 gRPC 原生不支持 long 类型）</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">ProtoContract(ImplicitFields = ImplicitFields.AllPublic)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ProtoLong</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">long</span> Value &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ProtoLong</span>()</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ProtoLong</span>(<span class="params"><span class="built_in">long</span> <span class="keyword">value</span></span>)</span> =&gt; Value = <span class="keyword">value</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">implicit</span> <span class="keyword">operator</span> <span class="title">ProtoLong</span>(<span class="params"><span class="built_in">long</span> <span class="keyword">value</span></span>)</span> =&gt; <span class="keyword">new</span>(<span class="keyword">value</span>);</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">implicit</span> <span class="keyword">operator</span> <span class="title">long</span>(<span class="params">ProtoLong result</span>)</span> =&gt; result.Value;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> gRPC 输出（标准化响应包装器）</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">ProtoContract(ImplicitFields = ImplicitFields.None)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">GrpcOutput</span>&lt;<span class="title">T</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line">    [<span class="meta">ProtoMember(1)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">bool</span> Success &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line"></span><br><span class="line">    [<span class="meta">ProtoMember(2)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Code &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line"></span><br><span class="line">    [<span class="meta">ProtoMember(3)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Msg &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line"></span><br><span class="line">    [<span class="meta">ProtoMember(4)</span>]</span><br><span class="line">    <span class="keyword">public</span> T Data &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="1-2-系统基础类型对照表"><a href="#1-2-系统基础类型对照表" class="headerlink" title="1.2 系统基础类型对照表"></a>1.2 系统基础类型对照表</h3><table><thead><tr><th>类型名称</th><th>原始类型</th><th>使用场景</th><th>序列化说明</th></tr></thead><tbody><tr><td><code>ProtoBoolean</code></td><td><code>bool</code></td><td>布尔值传输</td><td>直接映射<code>true</code>&#x2F;<code>false</code></td></tr><tr><td><code>ProtoDateTime</code></td><td><code>DateTime</code></td><td>日期时间传输（UTC格式）</td><td>使用 ISO8601 字符串格式</td></tr><tr><td><code>ProtoDecimal</code></td><td><code>decimal</code></td><td>高精度数值传输</td><td>转换为字符串避免精度丢失</td></tr><tr><td><code>ProtoInt</code></td><td><code>int</code></td><td>整型数值传输</td><td>直接映射 32 位整数</td></tr><tr><td><code>ProtoList&lt;T&gt;</code></td><td><code>List&lt;T&gt;</code></td><td>集合类型传输</td><td>支持泛型集合序列化</td></tr><tr><td><code>ProtoString</code></td><td><code>string</code></td><td>字符串传输</td><td>UTF-8 编码</td></tr><tr><td><code>ProtoLong</code></td><td><code>long</code></td><td>长整型传输</td><td>解决 int64 边界问题</td></tr><tr><td><code>GrpcOutput&lt;T&gt;</code></td><td><code>T</code></td><td>标准化响应格式</td><td>包含业务状态和数据的包装器</td></tr></tbody></table><hr><h2 id="2-请求-响应模型"><a href="#2-请求-响应模型" class="headerlink" title="2. 请求&#x2F;响应模型"></a>2. 请求&#x2F;响应模型</h2><h3 id="2-1-输入模型示例"><a href="#2-1-输入模型示例" class="headerlink" title="2.1 输入模型示例"></a>2.1 输入模型示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// MyCompanyName.Modules.Member.Api.Contracts/GrpcServices/Module/Input/ModuleAddGrpcInput.cs</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> ProtoBuf;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.Modules.Member.Api.Contracts.Services.Module.Input</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块创建请求参数</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">ProtoContract(ImplicitFields = ImplicitFields.None)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleAddGrpcInput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 模块名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">ProtoMember(1)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 创建时间</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">ProtoMember(2)</span>]</span><br><span class="line">    <span class="keyword">public</span> ProtoDateTime CreateTime &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-2-ImplicitFields-枚举说明"><a href="#2-2-ImplicitFields-枚举说明" class="headerlink" title="2.2 ImplicitFields 枚举说明"></a>2.2 ImplicitFields 枚举说明</h3><table><thead><tr><th>枚举成员</th><th>描述</th><th>推荐场景</th></tr></thead><tbody><tr><td><code>None</code></td><td>不进行隐式标记分配，所有成员需显式使用<code>[ProtoMember]</code> 属性指定标记</td><td><strong>推荐</strong>，提供最高稳定性和可控性，即使成员名称变化也不会影响序列化</td></tr><tr><td><code>AllPublic</code></td><td>所有公共成员（属性和字段）按字母顺序隐式分配标记，从<code>ImplicitFirstTag</code> 开始</td><td>当需要将公共 API 作为契约、且成员名称稳定不变时使用</td></tr><tr><td><code>AllFields</code></td><td>所有字段（包括公共和非公共字段）按字母顺序隐式分配标记</td><td>用于序列化内部状态，通常用于实现序列化而非公共 API</td></tr></tbody></table><blockquote><p><strong>使用建议</strong>：若需稳定的序列化和反序列化，推荐使用 <code>[ProtoMember]</code> 显式分配标记。若希望快速实现且成员名称不会频繁变化，可使用 <code>ImplicitFields.AllPublic</code>。</p></blockquote><h3 id="2-3-模型定义规范"><a href="#2-3-模型定义规范" class="headerlink" title="2.3 模型定义规范"></a>2.3 模型定义规范</h3><ul><li>所有 DTO 必须使用 <code>[ProtoContract]</code> 和 <code>[ProtoMember]</code> 标记</li><li><code>ProtoMember</code> 编号必须连续且唯一，从 <strong>1</strong> 开始递增</li><li><strong>禁止</strong>修改已发布字段的编号，新增成员使用后续数字</li><li>枚举类型必须指定 <code>[ProtoContract]</code> 和 <code>[ProtoMember(1)]</code></li></ul><hr><h2 id="3-服务接口定义"><a href="#3-服务接口定义" class="headerlink" title="3. 服务接口定义"></a>3. 服务接口定义</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// MyCompanyName.Modules.Member.Api.Contracts/GrpcServices/Module/IModuleGrpcService.cs</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Member.Api.Contracts.Services.Module.Input;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Member.Api.Core.Consts;</span><br><span class="line"><span class="keyword">using</span> ProtoBuf.Grpc;</span><br><span class="line"><span class="keyword">using</span> System.ServiceModel;</span><br><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Protos;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.Modules.Member.Api.Contracts.GrpcServices.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块 gRPC 服务接口</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">ServiceContract(ConfigurationName = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">IModuleGrpcService</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 创建模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;input&quot;&gt;</span>创建参数<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;context&quot;&gt;</span>调用上下文<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;returns&gt;</span>模块ID<span class="doctag">&lt;/returns&gt;</span></span></span><br><span class="line">    [<span class="meta">OperationContract</span>]</span><br><span class="line">    Task&lt;GrpcOutput&lt;ProtoLong&gt;&gt; AddAsync(ModuleAddGrpcInput input, CallContext context = <span class="literal">default</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="属性说明"><a href="#属性说明" class="headerlink" title="属性说明"></a>属性说明</h3><table><thead><tr><th>属性</th><th>说明</th></tr></thead><tbody><tr><td><code>ConfigurationName</code></td><td>用于自动化客户端生成和服务发现，值需与<code>ApiConsts.AreaName</code> 常量保持一致</td></tr><tr><td><code>OperationContract</code></td><td>可选，隐式继承自基类，标识可远程调用的服务方法</td></tr></tbody></table><hr><h2 id="4-服务实现示例"><a href="#4-服务实现示例" class="headerlink" title="4. 服务实现示例"></a>4. 服务实现示例</h2><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// MyCompanyName.Modules.Member.Api/GrpcServices/Module/ModuleGrpcService.cs</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> ProtoBuf.Grpc;</span><br><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Protos;</span><br><span class="line"><span class="keyword">using</span> Mapster;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Member.Api.Contracts.Services.Module;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Member.Api.Contracts.Services.Module.Input;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Member.Api.Contracts.GrpcServices.Module;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.Modules.Member.Api.GrpcServices.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleGrpcService</span> : <span class="title">IModuleGrpcService</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> IModuleService _moduleService;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleGrpcService</span>(<span class="params">IModuleService moduleService</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleService = moduleService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">async</span> Task&lt;GrpcOutput&lt;ProtoLong&gt;&gt; AddAsync(ModuleAddGrpcInput input, CallContext context)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">try</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">var</span> id = <span class="keyword">await</span> _moduleService.AddAsync(input.Adapt&lt;ModuleAddInput&gt;());</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> GrpcOutput&lt;ProtoLong&gt; &#123; Success = <span class="literal">true</span>, Data = id &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">catch</span> (Exception ex)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> GrpcOutput&lt;ProtoLong&gt; &#123; Code = <span class="string">&quot;&quot;</span>, Msg = ex.Message &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="5-跨模块调用-gRPC-服务"><a href="#5-跨模块调用-gRPC-服务" class="headerlink" title="5. 跨模块调用 gRPC 服务"></a>5. 跨模块调用 gRPC 服务</h2><h3 id="5-1-添加项目引用"><a href="#5-1-添加项目引用" class="headerlink" title="5.1 添加项目引用"></a>5.1 添加项目引用</h3><p>在 <code>MyCompanyName.Modules.Biz.Api</code> 接口库的 <code>.csproj</code> 文件中添加对目标模块契约项目的引用：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">ProjectReference</span> <span class="attr">Include</span>=<span class="string">&quot;..\..\MyCompanyName.Modules.Member\MyCompanyName.Modules.Member.Api.Contracts\MyCompanyName.Modules.Member.Api.Contracts.csproj&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="5-2-配置-appsettings-json"><a href="#5-2-配置-appsettings-json" class="headerlink" title="5.2 配置 appsettings.json"></a>5.2 配置 appsettings.json</h3><p>在 <code>MyCompanyName.Modules.Biz.Host</code> 项目的 <code>appsettings.json</code> 中添加 gRPC 配置：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;RpcConfig&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;Http&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Enable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;AssemblyNames&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span> <span class="string">&quot;ZhonTai.Admin.Contracts&quot;</span> <span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;Grpc&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Enable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;AssemblyNames&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="string">&quot;ZhonTai.Admin.Core&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="string">&quot;MyCompanyName.Modules.Member.Api.Contracts&quot;</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;ServerAssemblyNames&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;Endpoints&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;HttpUrl&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:18010&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;GrpcUrl&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:18011&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;mem&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;HttpUrl&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:18020&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;GrpcUrl&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:18021&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h4 id="配置项说明"><a href="#配置项说明" class="headerlink" title="配置项说明"></a>配置项说明</h4><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>Grpc.AssemblyNames</code></td><td>添加要扫描的 gRPC 客户端程序集（gRPC 服务接口所在的程序集）</td></tr><tr><td><code>Grpc.ServerAssemblyNames</code></td><td>添加要扫描的 gRPC 服务端程序集（gRPC 服务实现所在的程序集）</td></tr><tr><td><code>Endpoints</code></td><td>声明目标服务的通信地址</td></tr><tr><td><code>Name</code></td><td>服务标识符，需与契约项目中的服务名称对应</td></tr><tr><td><code>GrpcUrl</code></td><td>目标服务的 Grpc 端点地址</td></tr></tbody></table><h3 id="5-3-服务调用示例"><a href="#5-3-服务调用示例" class="headerlink" title="5.3 服务调用示例"></a>5.3 服务调用示例</h3><p>在业务服务中通过构造函数注入 gRPC 服务客户端：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> FreeScheduler;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Authorization;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Mvc;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Biz.Api.Contracts.Domain.Module;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Biz.Api.Contracts.Services.Module;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Biz.Api.Contracts.Services.Module.Input;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Biz.Api.Contracts.Services.Module.Output;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Biz.Api.Core.Consts;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Biz.Api.Core.Repositories;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.Modules.Member.Api.Contracts.GrpcServices.Module.Input;</span><br><span class="line"><span class="keyword">using</span> Newtonsoft.Json;</span><br><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> ZhonTai;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Dto;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Services;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.DynamicApi;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.DynamicApi.Attributes;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.Modules.Biz.Api.Services.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块服务</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">Order(1010)</span>]</span><br><span class="line">[<span class="meta">DynamicApi(Area = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">BaseService</span>, <span class="title">IModuleService</span>, <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> AppRepositoryBase&lt;ModuleEntity&gt; _moduleRep;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> IModuleGrpcService _moduleGrpcService;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params"></span></span></span><br><span class="line"><span class="params"><span class="function">        AppRepositoryBase&lt;ModuleEntity&gt; moduleRep,</span></span></span><br><span class="line"><span class="params"><span class="function">        IModuleGrpcService moduleGrpcService</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleRep = moduleRep;</span><br><span class="line">        _moduleGrpcService = moduleGrpcService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 查询模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 调用 gRPC 服务示例</span></span><br><span class="line">        <span class="keyword">var</span> grpcResult = <span class="keyword">await</span> _moduleGrpcService.AddAsync(<span class="keyword">new</span> ModuleAddGrpcInput</span><br><span class="line">        &#123;</span><br><span class="line">            Name = <span class="string">&quot;Grpc测试&quot;</span></span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> result = <span class="keyword">await</span> _moduleRep.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p><strong>建议</strong>：实际开发中结合框架提供的中间件进行统一的服务治理（如熔断、限流等）。</p></blockquote><hr><h2 id="6-测试验证流程"><a href="#6-测试验证流程" class="headerlink" title="6. 测试验证流程"></a>6. 测试验证流程</h2><h3 id="6-1-服务启动验证"><a href="#6-1-服务启动验证" class="headerlink" title="6.1 服务启动验证"></a>6.1 服务启动验证</h3><p>运行项目后，在命令台中看到以下内容则表示 Grpc 服务方法注册成功：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Added gRPC method &#x27;Add&#x27; to service &#x27;MyCompanyName.Modules.Member.Api.Contracts.GrpcServices.Module.ModuleGrpcService&#x27;.</span><br><span class="line">Method type: Unary, HTTP method: , route pattern: &#x27;/MyCompanyName.Modules.Member.Api.Contracts.GrpcServices.Module.ModuleGrpcService/Add&#x27;.</span><br></pre></td></tr></table></figure><h3 id="6-2-Postman-测试步骤"><a href="#6-2-Postman-测试步骤" class="headerlink" title="6.2 Postman 测试步骤"></a>6.2 Postman 测试步骤</h3><ol><li>点击 <strong>New</strong> 按钮，弹出对话框选择 <strong>gRPC</strong></li><li>在 Enter URL 文本框中输入 <code>grpc://localhost:18021</code>（URL 格式：<code>grpc://{host}:{port}</code>）</li><li>右侧下拉框中选择 <strong>Use Server Reflection</strong>，出现 Add 方法则表示反射成功</li><li>选择 <code>ModuleGrpcService/Add</code> 方法</li><li>构造请求报文：</li></ol><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;TestModule&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;CreateTime&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Value&quot;</span><span class="punctuation">:</span> <span class="string">&quot;2024-03-20T08:00:00Z&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><ol start="6"><li>点击 <strong>Invoke</strong> 按钮，验证响应结构：</li></ol><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;Success&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;Data&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Value&quot;</span><span class="punctuation">:</span> <span class="number">649219021422661</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><hr><h2 id="7-版本兼容策略"><a href="#7-版本兼容策略" class="headerlink" title="7. 版本兼容策略"></a>7. 版本兼容策略</h2><h3 id="7-1-变更类型处理"><a href="#7-1-变更类型处理" class="headerlink" title="7.1 变更类型处理"></a>7.1 变更类型处理</h3><table><thead><tr><th>变更类型</th><th align="center">允许性</th><th>处理方案</th></tr></thead><tbody><tr><td>新增字段</td><td align="center">✅允许</td><td>客户端需处理字段缺失情况</td></tr><tr><td>删除字段</td><td align="center">⚠️禁止</td><td>标记<code>Obsolete</code>，保留至少两个版本周期</td></tr><tr><td>修改字段类型</td><td align="center">⛔禁止</td><td>必须创建新版本接口</td></tr><tr><td>重命名字段</td><td align="center">⛔禁止</td><td>使用<code>[ProtoMember]</code> 别名功能实现平滑过渡</td></tr></tbody></table><h3 id="7-2-版本标识规则"><a href="#7-2-版本标识规则" class="headerlink" title="7.2 版本标识规则"></a>7.2 版本标识规则</h3><p>在服务契约中声明版本号：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">ServiceContract(Name = <span class="string">&quot;MemberService_v1&quot;</span>)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">IModuleGrpcService</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>#中台&#x2F;分布式微服务 #分布式 #分布式&#x2F;消息传递 #API接口 #中台&#x2F;配置文件</p>]]>
    </content>
    <id>https://zizai.cc/posts/grpc-dev-guide/</id>
    <link href="https://zizai.cc/posts/grpc-dev-guide/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>本文档为开发者提供标准化的 gRPC 接口开发指南，涵盖从协议定义、服务实现到测试验证的全流程规范。适用于 .NET 技术栈的微服务场景，确保跨服务通信的高效性、兼容性和可维护性。</summary>
    <title>gRPC 远程通讯开发指南</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="JSON" scheme="https://zizai.cc/tags/JSON/"/>
    <content>
      <![CDATA[<h3 id="反序列化"><a href="#反序列化" class="headerlink" title="反序列化"></a>反序列化</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 反序列化为强类型</span></span><br><span class="line"><span class="keyword">var</span> json = <span class="string">&quot;&#123;\&quot;name\&quot;:\&quot;张三\&quot;,\&quot;age\&quot;:25&#125;&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> user = JsonHelper.Deserialize&lt;UserDto&gt;(json);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 反序列化为动态类型</span></span><br><span class="line"><span class="keyword">var</span> obj = JsonHelper.Deserialize(json, <span class="keyword">typeof</span>(UserDto));</span><br></pre></td></tr></table></figure><h3 id="使用自定义选项"><a href="#使用自定义选项" class="headerlink" title="使用自定义选项"></a>使用自定义选项</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"><span class="keyword">using</span> System.Text.Json;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用自定义选项序列化</span></span><br><span class="line"><span class="keyword">var</span> options = <span class="keyword">new</span> JsonSerializerOptions</span><br><span class="line">&#123;</span><br><span class="line">    WriteIndented = <span class="literal">false</span>,</span><br><span class="line">    PropertyNamingPolicy = <span class="literal">null</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> json = JsonHelper.Serialize(user, options);</span><br></pre></td></tr></table></figure><h3 id="配置全局选项"><a href="#配置全局选项" class="headerlink" title="配置全局选项"></a>配置全局选项</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改全局默认配置（线程安全）</span></span><br><span class="line">JsonHelper.ConfigureOptions(options =&gt;</span><br><span class="line">&#123;</span><br><span class="line">    options.WriteIndented = <span class="literal">false</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="获取当前配置副本"><a href="#获取当前配置副本" class="headerlink" title="获取当前配置副本"></a>获取当前配置副本</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Common.Helpers;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取当前全局配置的副本，避免影响全局</span></span><br><span class="line"><span class="keyword">var</span> options = JsonHelper.GetCurrentOptions();</span><br><span class="line">options.WriteIndented = <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">var</span> json = JsonHelper.Serialize(user, options);</span><br></pre></td></tr></table></figure><h2 id="FlexibleEnumConverter-枚举兼容"><a href="#FlexibleEnumConverter-枚举兼容" class="headerlink" title="FlexibleEnumConverter 枚举兼容"></a>FlexibleEnumConverter 枚举兼容</h2><p><code>FlexibleEnumConverter</code> 允许枚举字段同时使用字符串或数字格式：</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 以下两种 JSON 格式均可正确反序列化</span></span><br><span class="line"><span class="keyword">var</span> json1 = <span class="string">&quot;&#123;\&quot;status\&quot;:\&quot;Enabled\&quot;&#125;&quot;</span>;    <span class="comment">// 字符串格式</span></span><br><span class="line"><span class="keyword">var</span> json2 = <span class="string">&quot;&#123;\&quot;status\&quot;:1&#125;&quot;</span>;               <span class="comment">// 数字格式</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> result1 = JsonHelper.Deserialize&lt;MyDto&gt;(json1);</span><br><span class="line"><span class="keyword">var</span> result2 = JsonHelper.Deserialize&lt;MyDto&gt;(json2);</span><br></pre></td></tr></table></figure><p>#中台 #中台&#x2F;.NET模板 #中台&#x2F;特性注解 #中台&#x2F;配置文件</p>]]>
    </content>
    <id>https://zizai.cc/posts/json-deserialize-helper/</id>
    <link href="https://zizai.cc/posts/json-deserialize-helper/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>FlexibleEnumConverter 允许枚举字段同时使用字符串或数字格式：</summary>
    <title>JSON 反序列化帮助类</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="Linux" scheme="https://zizai.cc/tags/Linux/"/>
    <content>
      <![CDATA[<h2 id="Linux-部署指南"><a href="#Linux-部署指南" class="headerlink" title="Linux 部署指南"></a>Linux 部署指南</h2><h3 id="一、生成发布文件"><a href="#一、生成发布文件" class="headerlink" title="一、生成发布文件"></a>一、生成发布文件</h3><ol><li>在 <code>*.Host</code> 项目上右键，选择 <strong>发布</strong></li><li>点击 <strong>新建配置文件</strong></li><li>选择目标文件夹及位置，完成相关配置后点击 <strong>发布</strong></li></ol><p>发布配置文件位于 <code>Properties/PublishProfiles/FolderProfile.pubxml</code>，内容如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- https://go.microsoft.com/fwlink/?LinkID=208121. --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">Project</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">PropertyGroup</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">DeleteExistingFiles</span>&gt;</span>false<span class="tag">&lt;/<span class="name">DeleteExistingFiles</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ExcludeApp_Data</span>&gt;</span>false<span class="tag">&lt;/<span class="name">ExcludeApp_Data</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">LaunchSiteAfterPublish</span>&gt;</span>true<span class="tag">&lt;/<span class="name">LaunchSiteAfterPublish</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">LastUsedBuildConfiguration</span>&gt;</span>Release<span class="tag">&lt;/<span class="name">LastUsedBuildConfiguration</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">LastUsedPlatform</span>&gt;</span>Any CPU<span class="tag">&lt;/<span class="name">LastUsedPlatform</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">PublishProvider</span>&gt;</span>FileSystem<span class="tag">&lt;/<span class="name">PublishProvider</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">PublishUrl</span>&gt;</span>bin\Release\net10.0\publish\<span class="tag">&lt;/<span class="name">PublishUrl</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">WebPublishMethod</span>&gt;</span>FileSystem<span class="tag">&lt;/<span class="name">WebPublishMethod</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">_TargetId</span>&gt;</span>Folder<span class="tag">&lt;/<span class="name">_TargetId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">SiteUrlToLaunchAfterPublish</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">TargetFramework</span>&gt;</span>net10.0<span class="tag">&lt;/<span class="name">TargetFramework</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">RuntimeIdentifier</span>&gt;</span>linux-x64<span class="tag">&lt;/<span class="name">RuntimeIdentifier</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ProjectGuid</span>&gt;</span>6f47a41a-085e-4422-bb73-5a2cbaa07d9f<span class="tag">&lt;/<span class="name">ProjectGuid</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">SelfContained</span>&gt;</span>false<span class="tag">&lt;/<span class="name">SelfContained</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">PropertyGroup</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">Project</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h3 id="二、添加-Docker-部署文件"><a href="#二、添加-Docker-部署文件" class="headerlink" title="二、添加 Docker 部署文件"></a>二、添加 Docker 部署文件</h3><h4 id="Dockerfile"><a href="#Dockerfile" class="headerlink" title="Dockerfile"></a>Dockerfile</h4><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用官方 ASP.NET Core 运行时镜像</span></span><br><span class="line"><span class="keyword">FROM</span> mcr.microsoft.com/dotnet/aspnet:<span class="number">10.0</span> AS publish</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置工作目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴露容器端口</span></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">18010</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 复制构建产物到容器</span></span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> . .</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置时区</span></span><br><span class="line"><span class="keyword">ENV</span> TZ=Asia/Shanghai</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动命令</span></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="language-bash"> [<span class="string">&quot;dotnet&quot;</span>, <span class="string">&quot;*.Host.dll&quot;</span>]</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>注意：</strong></p><ul><li><code>*.Host.dll</code> 需替换为实际项目编译后的文件名</li><li><code>18010</code> 需替换为实际使用的端口号</li></ul></blockquote><h4 id="docker-compose-yml"><a href="#docker-compose-yml" class="headerlink" title="docker-compose.yml"></a>docker-compose.yml</h4><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">&#x27;3&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line">  <span class="attr">admin:</span></span><br><span class="line">    <span class="attr">image:</span> <span class="string">zhontai/admin:10.0.1</span></span><br><span class="line">    <span class="attr">build:</span></span><br><span class="line">      <span class="attr">context:</span> <span class="string">.</span></span><br><span class="line">      <span class="attr">dockerfile:</span> <span class="string">Dockerfile</span></span><br><span class="line">    <span class="attr">hostname:</span> <span class="string">admin</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">admin</span></span><br><span class="line">    <span class="attr">ports:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;18010:18010&quot;</span></span><br><span class="line">    <span class="attr">volumes:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">./appsettings.json:/app/appsettings.json</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">./ConfigCenter:/app/ConfigCenter</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">../logs/:/logs</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">./upload/:/app/upload</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>配置说明：</strong></p><ul><li><code>ports</code>：宿主机端口 : 容器端口</li><li><code>volumes</code>：宿主机目录 : 容器目录</li></ul></blockquote><hr><h3 id="三、构建并启动容器"><a href="#三、构建并启动容器" class="headerlink" title="三、构建并启动容器"></a>三、构建并启动容器</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose up -d --build</span><br></pre></td></tr></table></figure><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>-d</code></td><td>后台运行容器</td></tr><tr><td><code>--build</code></td><td>强制重新构建镜像（适用于代码更新后）</td></tr></tbody></table><p>**访问接口文档：**<a href="http://localhost:18010/doc/admin/index.html">http://localhost:18010/doc/admin/index.html</a></p><blockquote><p><strong>提示：</strong> 若无法访问，请检查 <code>ConfigCenter/appconfig.json</code> 中的 <code>swagger.enabled</code> 配置项是否已开启。</p></blockquote><hr><h3 id="四、常用运维命令"><a href="#四、常用运维命令" class="headerlink" title="四、常用运维命令"></a>四、常用运维命令</h3><table><thead><tr><th>操作</th><th>命令</th></tr></thead><tbody><tr><td>查看服务状态</td><td><code>docker-compose ps</code></td></tr><tr><td>查看实时日志</td><td><code>docker-compose logs -f admin</code></td></tr><tr><td>停止并删除容器</td><td><code>docker-compose down</code></td></tr><tr><td>删除镜像</td><td><code>docker rmi zhontai/admin:10.0.1</code></td></tr><tr><td>强制删除镜像</td><td><code>docker rmi -f zhontai/admin:10.0.1</code></td></tr></tbody></table><p>#中台 #配置文件 #分布式&#x2F;容器化 #Windows</p>]]>
    </content>
    <id>https://zizai.cc/posts/linux-deploy-guide/</id>
    <link href="https://zizai.cc/posts/linux-deploy-guide/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>在 .Host 项目上右键，选择 发布</summary>
    <title>Linux 部署指南</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="即时通讯" scheme="https://zizai.cc/tags/%E5%8D%B3%E6%97%B6%E9%80%9A%E8%AE%AF/"/>
    <category term="模块" scheme="https://zizai.cc/tags/%E6%A8%A1%E5%9D%97/"/>
    <content>
      <![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>即时通讯模块基于 WebSocket 协议与 Redis 消息队列构建，提供客户端与服务端双向实时通信能力。该模块支持模块数据同步、用户实时通知等应用场景，通过标准化配置与事件驱动机制，实现消息低延迟传输及精准分发。</p><p>本文档聚焦模块刷新场景，详细说明配置规范、消息收发流程及开发注意事项，助力开发者快速完成业务集成。</p><hr><h2 id="1-配置说明"><a href="#1-配置说明" class="headerlink" title="1. 配置说明"></a>1. 配置说明</h2><h3 id="1-1-服务端配置"><a href="#1-1-服务端配置" class="headerlink" title="1.1 服务端配置"></a>1.1 服务端配置</h3><p><strong>文件路径</strong>：<code>MyCompanyName.Modules.ImServer.Host/appsettings.json</code></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;ImServerConfig&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;RedisClientConnectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;127.0.0.1:6379,password=,poolsize=10,defaultDatabase=6&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Servers&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;127.0.0.1:17010&quot;</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Server&quot;</span><span class="punctuation">:</span> <span class="string">&quot;127.0.0.1:17010&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;InputEncodingName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;GB2312&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;OutputEncodingName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;GB2312&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;HealthChecks&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;Enable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/health&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><strong>关键配置项说明</strong>：</p><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>RedisClientConnectionString</code></td><td>Redis 客户端连接字符串</td></tr><tr><td><code>Servers</code></td><td>集群节点地址列表（单机部署填写当前节点地址）</td></tr><tr><td><code>Server</code></td><td>当前服务节点标识</td></tr><tr><td><code>HealthChecks</code></td><td>健康检查配置</td></tr></tbody></table><h3 id="1-2-客户端配置"><a href="#1-2-客户端配置" class="headerlink" title="1.2 客户端配置"></a>1.2 客户端配置</h3><p><strong>文件路径</strong>：<code>MyCompanyName.Modules.System.Host/imconfig.json</code></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;ImConfig&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Enable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Servers&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;127.0.0.1:17010&quot;</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Server&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ws://127.0.0.1:17010&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;RedisConnectionString&quot;</span><span class="punctuation">:</span> <span class="string">&quot;127.0.0.1:6379,password=,defaultDatabase=6&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><strong>关键配置项说明</strong>：</p><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>Enable</code></td><td>是否启用 IM 功能</td></tr><tr><td><code>RedisConnectionString</code></td><td>需与服务端 Redis 配置完全一致</td></tr><tr><td><code>Server</code></td><td>WebSocket 连接地址，协议头需为<code>ws://</code> 或 <code>wss://</code></td></tr></tbody></table><hr><h2 id="2-消息推送机制"><a href="#2-消息推送机制" class="headerlink" title="2. 消息推送机制"></a>2. 消息推送机制</h2><h3 id="2-1-后端推送代码示例"><a href="#2-1-后端推送代码示例" class="headerlink" title="2.1 后端推送代码示例"></a>2.1 后端推送代码示例</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ModuleService.cs</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;<span class="built_in">long</span>&gt; <span class="title">AddAsync</span>(<span class="params">ModuleAddInput input</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// ...业务逻辑...</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (_imConfig.Enable)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 推送 IM 消息</span></span><br><span class="line">        ImHelper.SendMessage(</span><br><span class="line">            senderId: <span class="number">0</span>,                     <span class="comment">// 系统消息发送者 ID</span></span><br><span class="line">            receiveUserIds: [User.Id],        <span class="comment">// 接收用户 ID 列表</span></span><br><span class="line">            message: <span class="keyword">new</span></span><br><span class="line">            &#123;</span><br><span class="line">                evts = <span class="keyword">new</span>[]</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">new</span></span><br><span class="line">                    &#123;</span><br><span class="line">                        name = <span class="string">&quot;refreshModule&quot;</span>,  <span class="comment">// 事件名称</span></span><br><span class="line">                        data = <span class="keyword">new</span> &#123; &#125;           <span class="comment">// 事件数据（可扩展）</span></span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">throw</span> ResultOutput.Exception(_adminLocalizer[<span class="string">&quot;请开启im即时通讯&quot;</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-2-参数说明"><a href="#2-2-参数说明" class="headerlink" title="2.2 参数说明"></a>2.2 参数说明</h3><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>senderId</code></td><td>0 表示系统消息</td></tr><tr><td><code>receiveUserIds</code></td><td>指定接收用户 ID 列表（支持群发）</td></tr><tr><td><code>message</code></td><td>消息体需包含<code>evts</code> 事件数组</td></tr></tbody></table><hr><h2 id="3-消息接收机制"><a href="#3-消息接收机制" class="headerlink" title="3. 消息接收机制"></a>3. 消息接收机制</h2><h3 id="3-1-前端事件类型定义"><a href="#3-1-前端事件类型定义" class="headerlink" title="3.1 前端事件类型定义"></a>3.1 前端事件类型定义</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// types/mitt.d.ts</span></span><br><span class="line"><span class="keyword">declare</span> <span class="keyword">type</span> <span class="title class_">MittType</span>&lt;T = <span class="built_in">any</span>&gt; = &#123;</span><br><span class="line">  <span class="attr">refreshModule</span>?: T  <span class="comment">// 模块刷新事件</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-Vue-组件监听实现"><a href="#3-2-Vue-组件监听实现" class="headerlink" title="3.2 Vue 组件监听实现"></a>3.2 Vue 组件监听实现</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- sys/module/index.vue --&gt;</span><br><span class="line">&lt;script lang=&quot;ts&quot; setup&gt;</span><br><span class="line">import eventBus from &#x27;/@/utils/mitt&#x27;</span><br><span class="line"></span><br><span class="line">onMounted(() =&gt; &#123;</span><br><span class="line">  // 注册事件监听</span><br><span class="line">  eventBus.on(&#x27;refreshModule&#x27;, async () =&gt; &#123;</span><br><span class="line">    await reloadTable()      // 刷新表格数据</span><br><span class="line">    ElMessage.success(&#x27;模块已更新&#x27;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">onBeforeMount(() =&gt; &#123;</span><br><span class="line">  // 组件卸载时移除监听</span><br><span class="line">  eventBus.off(&#x27;refreshModule&#x27;)</span><br><span class="line">&#125;)</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h3 id="3-3-生命周期控制说明"><a href="#3-3-生命周期控制说明" class="headerlink" title="3.3 生命周期控制说明"></a>3.3 生命周期控制说明</h3><ul><li><code>onMounted</code>：初始化事件监听</li><li><code>onBeforeMount</code>：移除事件监听，避免内存泄漏</li><li>使用 <code>off()</code> 确保单例监听正确清理</li></ul><hr><h2 id="4-完整流程示例"><a href="#4-完整流程示例" class="headerlink" title="4. 完整流程示例"></a>4. 完整流程示例</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">前端提交请求</span><br><span class="line">      ↓</span><br><span class="line">业务后端处理</span><br><span class="line">      ↓</span><br><span class="line">IM 服务端存储消息（Redis 消息队列）</span><br><span class="line">      ↓</span><br><span class="line">Redis 确认推送</span><br><span class="line">      ↓</span><br><span class="line">IM 服务端推送消息（WebSocket）</span><br><span class="line">      ↓</span><br><span class="line">前端事件触发（mitt）</span><br><span class="line">      ↓</span><br><span class="line">前端重新请求数据</span><br><span class="line">      ↓</span><br><span class="line">业务后端返回最新数据</span><br></pre></td></tr></table></figure><p><strong>详细流程说明</strong>：</p><ol><li><strong>前端提交请求</strong>：前端向业务后端发送新增模块的请求</li><li><strong>业务后端处理</strong>：业务后端接收请求，通过 <code>ImHelper</code> 向 IM 服务端发送 <code>refreshModule</code> 事件</li><li><strong>IM 服务端存储消息</strong>：IM 服务端将消息存储到 Redis 的消息队列中</li><li><strong>Redis 确认推送</strong>：Redis 完成消息存储后，通知 IM 服务端消息推送已就绪</li><li><strong>IM 服务端推送消息</strong>：IM 服务端通过 WebSocket 将消息推送到前端</li><li><strong>前端事件触发</strong>：前端接收到消息后，通过 mitt 触发 <code>refreshModule</code> 事件</li><li><strong>前端重新请求数据</strong>：前端向业务后端重新请求模块列表数据</li><li><strong>业务后端返回数据</strong>：业务后端返回最新的模块列表数据给前端</li></ol><hr><h2 id="5-注意事项"><a href="#5-注意事项" class="headerlink" title="5. 注意事项"></a>5. 注意事项</h2><h3 id="5-1-Redis-一致性校验"><a href="#5-1-Redis-一致性校验" class="headerlink" title="5.1 Redis 一致性校验"></a>5.1 Redis 一致性校验</h3><p>确保服务端 <code>ImServerOptions.RedisClient</code> 与客户端 <code>ImConfig.RedisConnectionString</code> 的配置保持一致：</p><ul><li>数据库编号一致</li><li>密码配置一致</li><li>连接池配置合理</li></ul><h3 id="5-2-WebSocket-地址协议"><a href="#5-2-WebSocket-地址协议" class="headerlink" title="5.2 WebSocket 地址协议"></a>5.2 WebSocket 地址协议</h3><table><thead><tr><th>环境</th><th>协议</th><th>说明</th></tr></thead><tbody><tr><td>开发环境</td><td><code>ws://</code></td><td>非加密连接</td></tr><tr><td>生产环境</td><td><code>wss://</code></td><td>需配置 SSL 证书</td></tr></tbody></table><h3 id="5-3-事件命名规范"><a href="#5-3-事件命名规范" class="headerlink" title="5.3 事件命名规范"></a>5.3 事件命名规范</h3><p>建议使用 <code>[业务模块][操作类型]</code> 格式，例如：</p><ul><li><code>userLogout</code></li><li><code>orderStatusUpdate</code></li><li><code>refreshModule</code></li></ul><h3 id="5-4-性能优化"><a href="#5-4-性能优化" class="headerlink" title="5.4 性能优化"></a>5.4 性能优化</h3><ul><li>高频事件建议添加防抖处理</li><li>批量操作时合并事件推送</li></ul><h3 id="5-5-调试建议"><a href="#5-5-调试建议" class="headerlink" title="5.5 调试建议"></a>5.5 调试建议</h3><ul><li>使用 Redis 桌面工具监控消息队列</li><li>使用浏览器开发者工具查看 WebSocket 连接状态</li></ul><p>#消息队列 #Redis #分布式&#x2F;消息传递 #配置文件 #API接口</p>]]>
    </content>
    <id>https://zizai.cc/posts/im-module-tech-doc/</id>
    <link href="https://zizai.cc/posts/im-module-tech-doc/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>即时通讯模块基于 WebSocket 协议与 Redis 消息队列构建，提供客户端与服务端双向实时通信能力。该模块支持模块数据同步、用户实时通知等应用场景，通过标准化配置与事件驱动机制，实现消息低延迟传输及精准分发。</summary>
    <title>即时通讯模块（IM）技术文档</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <content>
      <![CDATA[<h2 id="项目介绍与应用场景"><a href="#项目介绍与应用场景" class="headerlink" title="项目介绍与应用场景"></a>项目介绍与应用场景</h2><p><code>ZhonTai.Template.App</code> 模板用于快速创建基于 <code>.NET</code> 的后端接口项目，支持 <strong>多模块分布式架构</strong> 和 <strong>单体应用</strong> 两种开发模式。</p><p>通过该模板，你可以一键生成结构清晰、配置规范的服务端项目。模板内置了以下基础能力：</p><ul><li>身份认证</li><li>权限控制</li><li>数据过滤</li><li>远程调用（gRPC &#x2F; HTTP）</li><li>操作日志</li><li>多数据库配置</li></ul><p>通过模板创建的项目，其框架核心功能（权限、日志、数据过滤、远程调用等）均引用自平台统一维护的 NuGet 包，而不是直接内嵌源码。</p><p>当平台发布新功能或修复问题后，你只需在项目中更新对应的 NuGet 包版本，即可获得最新能力，无需重新生成项目或手动合并代码，真正做到：</p><blockquote><p>一次生成，持续升级。</p></blockquote><h2 id="项目类型说明"><a href="#项目类型说明" class="headerlink" title="项目类型说明"></a>项目类型说明</h2><p>模板提供三种项目类型，用于匹配不同的职责边界。</p><table><thead><tr><th>类型</th><th>角色定位</th><th>何时使用</th></tr></thead><tbody><tr><td>平台端<code>sys</code></td><td>系统核心与权限管理中心</td><td>需要统一管理用户、角色、权限、日志时，作为整个系统的基座优先创建</td></tr><tr><td>业务端<code>app</code></td><td>专注具体业务处理的模块</td><td>当业务复杂，需要拆分为多个独立服务（如订单、商品、CMS），且依赖平台端的权限与用户信息时使用</td></tr><tr><td>用户端<code>mem</code></td><td>面向终端用户 &#x2F; 会员的接口</td><td>开发移动端、Web 前端等 C 端应用时使用。需要登录与数据隔离，但不涉及后台功能权限管理</td></tr></tbody></table><h2 id="核心应用场景"><a href="#核心应用场景" class="headerlink" title="核心应用场景"></a>核心应用场景</h2><h3 id="分布式微服务"><a href="#分布式微服务" class="headerlink" title="分布式微服务"></a>分布式微服务</h3><p>平台端提供统一的 gRPC 服务，业务端和用户端通过 gRPC 远程获取用户、权限、日志等信息。</p><p>各服务可连接独立数据库，实现：</p><ul><li>团队并行开发</li><li>服务独立部署</li><li>按业务模块独立扩容</li><li>权限能力统一复用</li></ul><h3 id="单体敏捷开发"><a href="#单体敏捷开发" class="headerlink" title="单体敏捷开发"></a>单体敏捷开发</h3><p>可通过 <code>--merge-db</code> 将权限库合并至业务库，在一个项目中完成全部功能。</p><p>适用于：</p><ul><li>中小型项目</li><li>快速交付场景</li><li>不需要复杂微服务拆分的系统</li></ul><h3 id="会员端快速搭建"><a href="#会员端快速搭建" class="headerlink" title="会员端快速搭建"></a>会员端快速搭建</h3><p>用户端项目省去了复杂的功能权限，只保留登录认证与数据权限能力。</p><p>适用于快速搭建：</p><ul><li>轻量级用户中心</li><li>小程序接口</li><li>移动端 API</li><li>C 端会员服务</li></ul><p>下面将从安装模板开始，详细说明各类型项目的创建方法与最佳实践。</p><hr><h1 id="安装或升级模板"><a href="#安装或升级模板" class="headerlink" title="安装或升级模板"></a>安装或升级模板</h1><h2 id="安装模板"><a href="#安装模板" class="headerlink" title="安装模板"></a>安装模板</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new install ZhonTai.Template.App</span><br></pre></td></tr></table></figure><blockquote><p>升级模板命令与安装模板命令相同。</p></blockquote><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><p>如果已经安装过 <code>ZhonTai.Template</code> 旧模板，需要先卸载旧模板，再安装 <code>ZhonTai.Template.App</code> 新模板，避免模板短名称 <code>MyApp</code> 冲突。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new uninstall ZhonTai.Template</span><br></pre></td></tr></table></figure><h2 id="安装指定版本"><a href="#安装指定版本" class="headerlink" title="安装指定版本"></a>安装指定版本</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new install ZhonTai.Template.App::10.0.5</span><br></pre></td></tr></table></figure><h2 id="查看帮助"><a href="#查看帮助" class="headerlink" title="查看帮助"></a>查看帮助</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -h</span><br></pre></td></tr></table></figure><h2 id="查看已安装模板"><a href="#查看已安装模板" class="headerlink" title="查看已安装模板"></a>查看已安装模板</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new list</span><br></pre></td></tr></table></figure><h2 id="卸载模板"><a href="#卸载模板" class="headerlink" title="卸载模板"></a>卸载模板</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new uninstall ZhonTai.Template.App</span><br></pre></td></tr></table></figure><hr><h1 id="新建平台端项目"><a href="#新建平台端项目" class="headerlink" title="新建平台端项目"></a>新建平台端项目</h1><h2 id="为什么需要平台端？"><a href="#为什么需要平台端？" class="headerlink" title="为什么需要平台端？"></a>为什么需要平台端？</h2><p>平台端是整个系统的“大脑”，负责统一管理后台基础功能，包括：</p><ul><li>用户</li><li>角色</li><li>菜单</li><li>权限</li><li>操作日志</li></ul><p>平台端对外提供 gRPC 服务，供业务端和用户端远程验证权限、获取用户信息。</p><p>任何需要后台权限管理的系统，都应优先创建平台端项目。</p><h2 id="何时使用平台端？"><a href="#何时使用平台端？" class="headerlink" title="何时使用平台端？"></a>何时使用平台端？</h2><p>适用于以下场景：</p><ul><li>启动一个全新的系统时，优先创建平台端，作为权限基座。</li><li>开发多模块分布式应用，平台端独立部署，其他模块通过 gRPC 依赖它。</li><li>需要为运营人员提供功能完整的后台管理界面（Admin）。</li></ul><h2 id="项目说明"><a href="#项目说明" class="headerlink" title="项目说明"></a>项目说明</h2><p>平台端项目接口包含：</p><ul><li>平台接口</li><li>Admin 权限管理接口</li></ul><p><code>dbconfig</code> 需要配置以下数据库：</p><table><thead><tr><th>数据库</th><th>说明</th></tr></thead><tbody><tr><td><code>sysdb</code></td><td>业务主库</td></tr><tr><td><code>admindb</code></td><td>权限数据库</td></tr><tr><td><code>logdb</code></td><td>日志库</td></tr></tbody></table><p>平台端提供以下 gRPC 服务接口：</p><ul><li>数据权限</li><li>用户权限</li><li>操作日志</li></ul><h2 id="设置项目编码和端口号"><a href="#设置项目编码和端口号" class="headerlink" title="设置项目编码和端口号"></a>设置项目编码和端口号</h2><h3 id="平台端项目"><a href="#平台端项目" class="headerlink" title="平台端项目"></a>平台端项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011</span><br></pre></td></tr></table></figure><h3 id="单体项目"><a href="#单体项目" class="headerlink" title="单体项目"></a>单体项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011</span><br></pre></td></tr></table></figure><h2 id="切换数据库为-MySQL"><a href="#切换数据库为-MySQL" class="headerlink" title="切换数据库为 MySQL"></a>切换数据库为 MySQL</h2><h3 id="平台端项目-1"><a href="#平台端项目-1" class="headerlink" title="平台端项目"></a>平台端项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011 -db MySql</span><br></pre></td></tr></table></figure><h3 id="单体项目-1"><a href="#单体项目-1" class="headerlink" title="单体项目"></a>单体项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011 -db MySql</span><br></pre></td></tr></table></figure><h2 id="合并权限数据库并切换为-MySQL"><a href="#合并权限数据库并切换为-MySQL" class="headerlink" title="合并权限数据库并切换为 MySQL"></a>合并权限数据库并切换为 MySQL</h2><p>将权限数据库 <code>admindb</code> 合并到业务库 <code>appdb</code> 中，同时将数据库切换为 MySQL：</p><h3 id="平台端项目-2"><a href="#平台端项目-2" class="headerlink" title="平台端项目"></a>平台端项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011 -md <span class="literal">true</span> -db MySql</span><br></pre></td></tr></table></figure><h3 id="单体项目-2"><a href="#单体项目-2" class="headerlink" title="单体项目"></a>单体项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011 -md <span class="literal">true</span> -db MySql</span><br></pre></td></tr></table></figure><h2 id="多模块开发推荐配置"><a href="#多模块开发推荐配置" class="headerlink" title="多模块开发推荐配置"></a>多模块开发推荐配置</h2><p>如果你已经熟悉项目结构，不需要生成模块示例，也不想增加测试库，可以使用以下配置：</p><h3 id="平台端项目-3"><a href="#平台端项目-3" class="headerlink" title="平台端项目"></a>平台端项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011 -nt <span class="literal">true</span> -ns <span class="literal">true</span> -db MySql</span><br></pre></td></tr></table></figure><h3 id="单体项目-3"><a href="#单体项目-3" class="headerlink" title="单体项目"></a>单体项目</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MySys -at sys -ac sys -p 18010 -gp 18011 -nt <span class="literal">true</span> -ns <span class="literal">true</span> -db MySql</span><br></pre></td></tr></table></figure><h2 id="温馨提示"><a href="#温馨提示" class="headerlink" title="温馨提示"></a>温馨提示</h2><ul><li>平台端项目端口号默认范围：<code>18010 - 18999</code></li><li>单体项目端口号默认范围：<code>16010 - 16999</code></li><li><code>gp</code> 端口号默认范围：<code>port + 1</code></li></ul><hr><h1 id="新建业务端项目"><a href="#新建业务端项目" class="headerlink" title="新建业务端项目"></a>新建业务端项目</h1><h2 id="为什么需要业务端？"><a href="#为什么需要业务端？" class="headerlink" title="为什么需要业务端？"></a>为什么需要业务端？</h2><p>当系统功能逐渐复杂时，如果所有代码都堆在平台端，项目会变得难以维护。</p><p>业务端项目可以将具体业务模块剥离为独立服务，例如：</p><ul><li>订单服务</li><li>商品服务</li><li>报表服务</li><li>CMS 服务</li></ul><p>业务端不包含 Admin 管理界面，但可以通过 gRPC 复用平台端的登录、权限与用户数据，在保持数据隔离的同时实现安全协作。</p><h2 id="何时使用业务端？"><a href="#何时使用业务端？" class="headerlink" title="何时使用业务端？"></a>何时使用业务端？</h2><p>适用于以下场景：</p><ul><li>平台端已经就绪，需要扩展新的业务功能模块。</li><li>需要从平台端拆分出独立业务服务，例如“电商业务服务”。</li><li>团队并行开发，多个业务端服务可同时开发，互不干扰。</li><li>需要独立部署并按业务模块伸缩，例如订单服务负载较高时可单独扩容。</li></ul><h2 id="设置项目编码和端口号-1"><a href="#设置项目编码和端口号-1" class="headerlink" title="设置项目编码和端口号"></a>设置项目编码和端口号</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MyBiz -at app -ac biz -p 19010 -gp 19011</span><br></pre></td></tr></table></figure><h2 id="切换数据库为-MySQL-1"><a href="#切换数据库为-MySQL-1" class="headerlink" title="切换数据库为 MySQL"></a>切换数据库为 MySQL</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MyBiz -at app -ac biz -p 19010 -gp 19011 -db MySql</span><br></pre></td></tr></table></figure><h2 id="多模块开发推荐配置-1"><a href="#多模块开发推荐配置-1" class="headerlink" title="多模块开发推荐配置"></a>多模块开发推荐配置</h2><p>如果你已经熟悉项目结构，不需要生成模块示例，也不想增加测试库，可以使用以下配置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MyBiz -at app -ac biz -p 19010 -gp 19011 -nt <span class="literal">true</span> -ns <span class="literal">true</span> -db MySql</span><br></pre></td></tr></table></figure><h2 id="温馨提示-1"><a href="#温馨提示-1" class="headerlink" title="温馨提示"></a>温馨提示</h2><ul><li>业务端项目端口号默认范围：<code>19010 - 19999</code></li><li><code>gp</code> 端口号默认范围：<code>port + 1</code></li></ul><hr><h1 id="新建用户端项目"><a href="#新建用户端项目" class="headerlink" title="新建用户端项目"></a>新建用户端项目</h1><h2 id="为什么需要用户端？"><a href="#为什么需要用户端？" class="headerlink" title="为什么需要用户端？"></a>为什么需要用户端？</h2><p>移动 App、微信小程序、官网等 C 端应用通常只需要用户注册登录和个人数据管理，并不需要复杂的后台角色、菜单和功能权限控制。</p><p>用户端项目去掉了功能权限限制，聚焦于：</p><ul><li>用户身份认证</li><li>数据权限控制，即用户只能操作自己的数据</li></ul><p>它适合提供轻量、安全的对外 API。</p><h2 id="何时使用用户端？"><a href="#何时使用用户端？" class="headerlink" title="何时使用用户端？"></a>何时使用用户端？</h2><p>适用于以下场景：</p><ul><li>构建面向终端消费者的会员中心。</li><li>开发小程序接口、移动端 API。</li><li>需要独立的用户端服务，并与后台管理系统物理隔离，以提升安全性。</li><li>配合平台端与业务端，为用户提供登录、信息修改、订单查询等前端接口。</li></ul><h2 id="设置项目编码和端口号-2"><a href="#设置项目编码和端口号-2" class="headerlink" title="设置项目编码和端口号"></a>设置项目编码和端口号</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MyMem -at mem -ac mem -p 20010 -gp 20011</span><br></pre></td></tr></table></figure><h2 id="切换数据库为-MySQL-2"><a href="#切换数据库为-MySQL-2" class="headerlink" title="切换数据库为 MySQL"></a>切换数据库为 MySQL</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MyMem -at mem -ac mem -p 20010 -gp 20011 -db MySql</span><br></pre></td></tr></table></figure><h2 id="多模块开发推荐配置-2"><a href="#多模块开发推荐配置-2" class="headerlink" title="多模块开发推荐配置"></a>多模块开发推荐配置</h2><p>如果你已经熟悉项目结构，不需要生成模块示例，也不想增加测试库，可以使用以下配置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyApp -n MyCompanyName.Module.MyMem -at mem -ac mem -p 20010 -gp 20011 -nt <span class="literal">true</span> -ns <span class="literal">true</span> -db MySql</span><br></pre></td></tr></table></figure><h2 id="温馨提示-2"><a href="#温馨提示-2" class="headerlink" title="温馨提示"></a>温馨提示</h2><ul><li>会员端项目端口号默认范围：<code>20010 - 20999</code></li><li><code>gp</code> 端口号默认范围：<code>port + 1</code></li></ul><hr><h1 id="源码项目配置提示"><a href="#源码项目配置提示" class="headerlink" title="源码项目配置提示"></a>源码项目配置提示</h1><p>使用源码项目时，请记得修改以下配置文件中的 <code>securityKey</code> 密钥：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ZhonTai.Admin.Host/ConfigCenter/jwtconfig.json</span><br></pre></td></tr></table></figure><p>示例配置如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;JwtConfig&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="comment">// 密钥，注意字符串长度不能低于 32 位</span></span><br><span class="line">    <span class="attr">&quot;securityKey&quot;</span><span class="punctuation">:</span> <span class="string">&quot;33ce0d4b3a7b11ef8563526747b33ad4&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><blockquote><p>注意：<code>securityKey</code> 必须妥善保管，生产环境建议使用安全、随机且长度足够的密钥。</p></blockquote><p>#中台 #中台&#x2F;搭建项目框架 #中台&#x2F;新建接口项目 #中台&#x2F;.NET模板 #中台&#x2F;分布式微服务</p>]]>
    </content>
    <id>https://zizai.cc/posts/new-app-interface/</id>
    <link href="https://zizai.cc/posts/new-app-interface/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>ZhonTai.Template.App 模板用于快速创建基于 .NET 的后端接口项目，支持 多模块分布式架构 和 单体应用 两种开发模式。</summary>
    <title>新建 App 接口项目</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="网关" scheme="https://zizai.cc/tags/%E7%BD%91%E5%85%B3/"/>
    <content>
      <![CDATA[<h2 id="项目介绍"><a href="#项目介绍" class="headerlink" title="项目介绍"></a>项目介绍</h2><p><code>Yarp Gateway</code> 是基于 <a href="https://microsoft.github.io/reverse-proxy/">YARP</a> 构建的 API 网关项目模板，主要用于统一接入后端服务，实现请求转发、负载均衡、动态配置等能力。</p><h2 id="功能特性"><a href="#功能特性" class="headerlink" title="功能特性"></a>功能特性</h2><h3 id="1-反向代理"><a href="#1-反向代理" class="headerlink" title="1. 反向代理"></a>1. 反向代理</h3><p>网关可以接收客户端请求，并根据路由规则将请求转发到对应的后端服务。</p><p>例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">客户端请求：/api/admin/user</span><br><span class="line">网关转发：http://localhost:18010/api/admin/user</span><br></pre></td></tr></table></figure><h3 id="2-负载均衡"><a href="#2-负载均衡" class="headerlink" title="2. 负载均衡"></a>2. 负载均衡</h3><p>支持在多个后端服务实例之间分配请求，提高系统的可扩展性和可靠性。</p><p>支持的负载均衡策略可参考 YARP 官方文档，常见策略包括：</p><ul><li><code>RoundRobin</code></li><li><code>Random</code></li><li><code>LeastRequests</code></li><li><code>PowerOfTwoChoices</code></li><li><code>FirstAlphabetical</code></li></ul><p>示例：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;LoadBalancingPolicy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;RoundRobin&quot;</span></span><br></pre></td></tr></table></figure><h3 id="3-动态配置"><a href="#3-动态配置" class="headerlink" title="3. 动态配置"></a>3. 动态配置</h3><p>支持在线动态添加和修改以下配置：</p><ul><li>集群配置</li><li>路由绑定</li><li>域名绑定</li><li>HTTPS 证书配置</li></ul><p>配置修改后可即时生效，无需重启网关服务。</p><blockquote><p>更多功能请参考 YARP 官方文档：<br><a href="https://microsoft.github.io/reverse-proxy/">https://microsoft.github.io/reverse-proxy/</a></p></blockquote><hr><h1 id="安装或升级模板"><a href="#安装或升级模板" class="headerlink" title="安装或升级模板"></a>安装或升级模板</h1><h2 id="安装模板"><a href="#安装模板" class="headerlink" title="安装模板"></a>安装模板</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new install ZhonTai.Template.Gateway</span><br></pre></td></tr></table></figure><blockquote><p>升级模板命令与安装模板命令相同。</p></blockquote><h2 id="安装指定版本"><a href="#安装指定版本" class="headerlink" title="安装指定版本"></a>安装指定版本</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new install ZhonTai.Template.Gateway::10.1.0</span><br></pre></td></tr></table></figure><h2 id="查看模板帮助"><a href="#查看模板帮助" class="headerlink" title="查看模板帮助"></a>查看模板帮助</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyGateway -h</span><br></pre></td></tr></table></figure><p>模板参数说明：</p><table><thead><tr><th>参数</th><th>简写</th><th>类型</th><th>默认值</th><th>说明</th></tr></thead><tbody><tr><td><code>--port</code></td><td><code>-p</code></td><td><code>int</code></td><td><code>16010</code></td><td>网关启动端口</td></tr></tbody></table><p>示例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyGateway -h</span><br></pre></td></tr></table></figure><p>输出示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-p, --port port   Port settings</span><br><span class="line">                    类型: int</span><br><span class="line">                    默认: 16010</span><br></pre></td></tr></table></figure><h2 id="卸载模板"><a href="#卸载模板" class="headerlink" title="卸载模板"></a>卸载模板</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new uninstall ZhonTai.Template.Gateway</span><br></pre></td></tr></table></figure><hr><h1 id="创建网关项目"><a href="#创建网关项目" class="headerlink" title="创建网关项目"></a>创建网关项目</h1><h2 id="使用默认端口创建项目"><a href="#使用默认端口创建项目" class="headerlink" title="使用默认端口创建项目"></a>使用默认端口创建项目</h2><p>默认网关端口为 <code>16010</code>。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyGateway -n MyCompanyName.MyGateway</span><br></pre></td></tr></table></figure><h2 id="指定网关端口创建项目"><a href="#指定网关端口创建项目" class="headerlink" title="指定网关端口创建项目"></a>指定网关端口创建项目</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet new MyGateway -n MyCompanyName.MyGateway -p 16010</span><br></pre></td></tr></table></figure><p>参数说明：</p><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>-n</code></td><td>指定项目名称</td></tr><tr><td><code>-p</code></td><td>指定网关启动端口</td></tr></tbody></table><hr><h1 id="配置文件说明"><a href="#配置文件说明" class="headerlink" title="配置文件说明"></a>配置文件说明</h1><h2 id="appsettings-json"><a href="#appsettings-json" class="headerlink" title="appsettings.json"></a>appsettings.json</h2><p><code>appsettings.json</code> 是应用程序主配置文件，主要包含以下配置：</p><ul><li>日志配置</li><li>服务监听地址</li><li>YARP 路由配置</li><li>YARP 集群配置</li><li>网关模块配置</li><li>健康检查配置</li></ul><p>示例配置如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;Logging&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;LogLevel&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;Default&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Information&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;Microsoft.AspNetCore&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Warning&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;AllowedHosts&quot;</span><span class="punctuation">:</span> <span class="string">&quot;*&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;Urls&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://*:16010&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;ReverseProxy&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Routes&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;admin&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/admin/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;admin-doc&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/admin/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;admin-file&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/upload/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;dev&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;dev&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/dev/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;dev-doc&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;dev&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/dev/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;sys&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/sys/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;sys-doc&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/sys/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;biz&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;biz&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/biz/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;biz-doc&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;biz&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/biz/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;mem&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;mem&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/mem/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;mem-doc&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;mem&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/mem/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Clusters&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;admin&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Destinations&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;destination1&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;Address&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:18010&quot;</span></span><br><span class="line">          <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;LoadBalancingPolicy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;RoundRobin&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;dev&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Destinations&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;destination1&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;Address&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:18020&quot;</span></span><br><span class="line">          <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;LoadBalancingPolicy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;RoundRobin&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;biz&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Destinations&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;destination1&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;Address&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:19010&quot;</span></span><br><span class="line">          <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;LoadBalancingPolicy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;RoundRobin&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;mem&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Destinations&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;destination1&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;Address&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:20010&quot;</span></span><br><span class="line">          <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;LoadBalancingPolicy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;RoundRobin&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;GatewayConfig&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;ModuleList&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;权限接口文档&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/admin/index.html&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;系统接口文档&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/sys/index.html&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;开发接口文档&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/dev/index.html&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;业务接口文档&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/biz/index.html&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;会员接口文档&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;Url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/mem/index.html&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;HealthChecks&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;Enable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/health&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><hr><h1 id="appsettings-json-配置说明"><a href="#appsettings-json-配置说明" class="headerlink" title="appsettings.json 配置说明"></a>appsettings.json 配置说明</h1><h2 id="基础配置"><a href="#基础配置" class="headerlink" title="基础配置"></a>基础配置</h2><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;AllowedHosts&quot;</span><span class="punctuation">:</span> <span class="string">&quot;*&quot;</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">&quot;Urls&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://*:16010&quot;</span></span><br></pre></td></tr></table></figure><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>AllowedHosts</code></td><td>允许访问的主机名，<code>*</code> 表示允许所有主机</td></tr><tr><td><code>Urls</code></td><td>网关监听地址和端口</td></tr></tbody></table><hr><h2 id="ReverseProxy-配置"><a href="#ReverseProxy-配置" class="headerlink" title="ReverseProxy 配置"></a>ReverseProxy 配置</h2><p><code>ReverseProxy</code> 是 YARP 的核心配置节点，包含：</p><ul><li><code>Routes</code>：路由配置</li><li><code>Clusters</code>：集群配置</li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;ReverseProxy&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;Routes&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;Clusters&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><hr><h2 id="Routes-路由配置"><a href="#Routes-路由配置" class="headerlink" title="Routes 路由配置"></a>Routes 路由配置</h2><p>路由用于定义客户端请求如何匹配到后端服务集群。</p><p>示例：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;admin&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;ClusterId&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;Match&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/admin/&#123;**catch-all&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Hosts&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>配置说明：</p><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>admin</code></td><td>路由名称，全局唯一</td></tr><tr><td><code>ClusterId</code></td><td>对应的集群 ID</td></tr><tr><td><code>Match.Path</code></td><td>请求路径匹配规则</td></tr><tr><td><code>Match.Hosts</code></td><td>主机匹配规则，空数组表示不限制 Host</td></tr></tbody></table><h3 id="路径匹配示例"><a href="#路径匹配示例" class="headerlink" title="路径匹配示例"></a>路径匹配示例</h3><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/api/admin/&#123;**catch-all&#125;&quot;</span></span><br></pre></td></tr></table></figure><p>表示匹配所有以 <code>/api/admin/</code> 开头的请求。</p><p>例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">/api/admin/user</span><br><span class="line">/api/admin/role/page</span><br><span class="line">/api/admin/menu/tree</span><br></pre></td></tr></table></figure><p>都会匹配该路由。</p><hr><h2 id="Clusters-集群配置"><a href="#Clusters-集群配置" class="headerlink" title="Clusters 集群配置"></a>Clusters 集群配置</h2><p>集群用于定义后端服务地址和负载均衡策略。</p><p>示例：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;admin&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;Destinations&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;destination1&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;Address&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:18010&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;LoadBalancingPolicy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;RoundRobin&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>配置说明：</p><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>admin</code></td><td>集群名称，需要与路由中的<code>ClusterId</code> 对应</td></tr><tr><td><code>Destinations</code></td><td>后端服务地址列表</td></tr><tr><td><code>Address</code></td><td>后端服务地址</td></tr><tr><td><code>LoadBalancingPolicy</code></td><td>负载均衡策略</td></tr></tbody></table><hr><h1 id="模块路由说明"><a href="#模块路由说明" class="headerlink" title="模块路由说明"></a>模块路由说明</h1><p>当前示例中配置了以下模块：</p><table><thead><tr><th>模块</th><th>路由路径</th><th>文档路径</th><th>后端服务地址</th></tr></thead><tbody><tr><td>权限管理</td><td><code>/api/admin/{**catch-all}</code></td><td><code>/doc/admin/{**catch-all}</code></td><td><code>http://localhost:18010</code></td></tr><tr><td>系统管理</td><td><code>/api/sys/{**catch-all}</code></td><td><code>/doc/sys/{**catch-all}</code></td><td><code>http://localhost:18010</code></td></tr><tr><td>开发管理</td><td><code>/api/dev/{**catch-all}</code></td><td><code>/doc/dev/{**catch-all}</code></td><td><code>http://localhost:18020</code></td></tr><tr><td>业务管理</td><td><code>/api/biz/{**catch-all}</code></td><td><code>/doc/biz/{**catch-all}</code></td><td><code>http://localhost:19010</code></td></tr><tr><td>会员管理</td><td><code>/api/mem/{**catch-all}</code></td><td><code>/doc/mem/{**catch-all}</code></td><td><code>http://localhost:20010</code></td></tr></tbody></table><hr><h1 id="GatewayConfig-网关配置"><a href="#GatewayConfig-网关配置" class="headerlink" title="GatewayConfig 网关配置"></a>GatewayConfig 网关配置</h1><p><code>GatewayConfig</code> 用于配置网关自身功能。</p><h2 id="ModuleList-模块列表"><a href="#ModuleList-模块列表" class="headerlink" title="ModuleList 模块列表"></a>ModuleList 模块列表</h2><p>用于配置网关首页或接口文档入口。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;ModuleList&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">  <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;权限接口文档&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/admin/index.html&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;Name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;系统接口文档&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;Url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/doc/sys/index.html&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><p>配置说明：</p><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>Name</code></td><td>文档名称</td></tr><tr><td><code>Url</code></td><td>文档访问地址</td></tr></tbody></table><hr><h2 id="HealthChecks-健康检查"><a href="#HealthChecks-健康检查" class="headerlink" title="HealthChecks 健康检查"></a>HealthChecks 健康检查</h2><p>用于配置网关健康检查接口。</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;HealthChecks&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;Enable&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;Path&quot;</span><span class="punctuation">:</span> <span class="string">&quot;/health&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>配置说明：</p><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>Enable</code></td><td>是否启用健康检查</td></tr><tr><td><code>Path</code></td><td>健康检查访问路径</td></tr></tbody></table><p>健康检查访问地址示例：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:16010/health</span><br></pre></td></tr></table></figure><hr><h1 id="launchSettings-json"><a href="#launchSettings-json" class="headerlink" title="launchSettings.json"></a>launchSettings.json</h1><p><code>launchSettings.json</code> 是开发环境启动配置文件，主要用于配置：</p><ul><li>启动方式</li><li>启动端口</li><li>环境变量</li><li>是否启动浏览器</li><li>IIS Express 配置</li></ul><p>示例配置：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;profiles&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;MyGateway.Host&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;commandName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Project&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;launchBrowser&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;environmentVariables&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ASPNETCORE_ENVIRONMENT&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Development&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;dotnetRunMessages&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;applicationUrl&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:16010&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;IIS Express&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;commandName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;IISExpress&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;launchBrowser&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;environmentVariables&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;ASPNETCORE_ENVIRONMENT&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Development&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;$schema&quot;</span><span class="punctuation">:</span> <span class="string">&quot;https://json.schemastore.org/launchsettings.json&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;iisSettings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;windowsAuthentication&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;anonymousAuthentication&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;iisExpress&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;applicationUrl&quot;</span><span class="punctuation">:</span> <span class="string">&quot;http://localhost:16010&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;sslPort&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h2 id="launchSettings-json-配置说明"><a href="#launchSettings-json-配置说明" class="headerlink" title="launchSettings.json 配置说明"></a>launchSettings.json 配置说明</h2><table><thead><tr><th>配置项</th><th>说明</th></tr></thead><tbody><tr><td><code>profiles</code></td><td>启动配置集合</td></tr><tr><td><code>commandName</code></td><td>启动方式，<code>Project</code> 表示直接启动项目</td></tr><tr><td><code>launchBrowser</code></td><td>启动时是否自动打开浏览器</td></tr><tr><td><code>environmentVariables</code></td><td>环境变量配置</td></tr><tr><td><code>ASPNETCORE_ENVIRONMENT</code></td><td>ASP.NET Core 运行环境</td></tr><tr><td><code>applicationUrl</code></td><td>应用启动地址</td></tr><tr><td><code>iisSettings</code></td><td>IIS Express 配置</td></tr></tbody></table><hr><h1 id="启动项目"><a href="#启动项目" class="headerlink" title="启动项目"></a>启动项目</h1><p>进入项目目录后执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dotnet run</span><br></pre></td></tr></table></figure><p>启动成功后访问：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:16010</span><br></pre></td></tr></table></figure><p>健康检查地址：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:16010/health</span><br></pre></td></tr></table></figure><hr><h1 id="常用命令汇总"><a href="#常用命令汇总" class="headerlink" title="常用命令汇总"></a>常用命令汇总</h1><table><thead><tr><th>操作</th><th>命令</th></tr></thead><tbody><tr><td>安装模板</td><td><code>dotnet new install ZhonTai.Template.Gateway</code></td></tr><tr><td>安装指定版本</td><td><code>dotnet new install ZhonTai.Template.Gateway::10.1.0</code></td></tr><tr><td>升级模板</td><td><code>dotnet new install ZhonTai.Template.Gateway</code></td></tr><tr><td>查看帮助</td><td><code>dotnet new MyGateway -h</code></td></tr><tr><td>卸载模板</td><td><code>dotnet new uninstall ZhonTai.Template.Gateway</code></td></tr><tr><td>创建项目</td><td><code>dotnet new MyGateway -n MyCompanyName.MyGateway</code></td></tr><tr><td>指定端口创建项目</td><td><code>dotnet new MyGateway -n MyCompanyName.MyGateway -p 16010</code></td></tr><tr><td>启动项目</td><td><code>dotnet run</code></td></tr></tbody></table><hr><h1 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h1><ul><li>YARP 官方文档：<br><a href="https://microsoft.github.io/reverse-proxy/">https://microsoft.github.io/reverse-proxy/</a></li><li>.NET CLI 模板命令文档：<br><a href="https://learn.microsoft.com/dotnet/core/tools/dotnet-new">https://learn.microsoft.com/dotnet/core/tools/dotnet-new</a></li></ul><p>#中台&#x2F;搭建网关</p>]]>
    </content>
    <id>https://zizai.cc/posts/new-gateway-project/</id>
    <link href="https://zizai.cc/posts/new-gateway-project/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>Yarp Gateway 是基于 YARPhttps://microsoft.github.io/reverse-proxy/ 构建的 API 网关项目模板，主要用于统一接入后端服务，实现请求转发、负载均衡、动态配置等能力。</summary>
    <title>新建 Gateway 网关项目</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="模块" scheme="https://zizai.cc/tags/%E6%A8%A1%E5%9D%97/"/>
    <content>
      <![CDATA[<p>本文以 <code>Module</code> 模块为例，说明如何新建模块实体类、输入输出 DTO 以及模块服务接口。</p><blockquote><p>约定：<code>Module</code> 可替换为实际业务模块名称。</p></blockquote><hr><h2 id="1-新建模块实体类"><a href="#1-新建模块实体类" class="headerlink" title="1. 新建模块实体类"></a>1. 新建模块实体类</h2><p>在以下目录中新建 <code>Module</code> 目录：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api.Contracts\Domain</span><br></pre></td></tr></table></figure><p>然后在 <code>Module</code> 目录下新建实体类 <code>ModuleEntity</code>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System;</span><br><span class="line"><span class="keyword">using</span> FreeSql.DataAnnotations;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Entities;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Contracts.Core.Consts;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Domain.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">Table(Name = DbConsts.TableNamePrefix + <span class="string">&quot;module&quot;</span>)</span>]</span><br><span class="line">[<span class="meta">Index(<span class="string">&quot;idx_&#123;tablename&#125;_01&quot;</span>, nameof(TenantId) + <span class="string">&quot;,&quot;</span> + nameof(Name), true)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">ModuleEntity</span> : <span class="title">EntityTenant</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">Column(StringLength = 50)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="约定说明"><a href="#约定说明" class="headerlink" title="约定说明"></a>约定说明</h3><ul><li>实体类以 <code>Entity</code> 结尾，例如：<code>ModuleEntity</code>。</li><li>表名和索引采用下划线命名法。</li><li>表名前缀统一使用 <code>DbConsts.TableNamePrefix</code>。</li><li>索引名称以 <code>idx</code> 开头。</li><li>继承 <code>EntityTenant</code> 后，查询数据时会自动根据租户编号 <code>TenantId</code> 进行数据筛选。</li></ul><hr><h2 id="2-新建输入输出-DTO"><a href="#2-新建输入输出-DTO" class="headerlink" title="2. 新建输入输出 DTO"></a>2. 新建输入输出 DTO</h2><p>在以下目录下新建 <code>Input</code> 和 <code>Output</code> 目录：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api.Contracts\Services</span><br></pre></td></tr></table></figure><p>推荐目录结构如下：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api.Contracts\Services\Module\Input</span><br><span class="line">MyCompanyName.MyProjectName.Api.Contracts\Services\Module\Output</span><br></pre></td></tr></table></figure><hr><h3 id="2-1-新建添加输入-DTO"><a href="#2-1-新建添加输入-DTO" class="headerlink" title="2.1 新建添加输入 DTO"></a>2.1 新建添加输入 DTO</h3><p>在 <code>Input</code> 目录下新建 <code>ModuleAddInput</code>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 添加模块</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleAddInput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="2-2-新建修改输入-DTO"><a href="#2-2-新建修改输入-DTO" class="headerlink" title="2.2 新建修改输入 DTO"></a>2.2 新建修改输入 DTO</h3><p>在 <code>Input</code> 目录下新建 <code>ModuleUpdateInput</code>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.ComponentModel.DataAnnotations;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Validators;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 修改模块</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">ModuleUpdateInput</span> : <span class="title">ModuleAddInput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 编号</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">Required</span>]</span><br><span class="line">    [<span class="meta">ValidateRequired(<span class="string">&quot;请选择模块&quot;</span>)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">long</span> Id &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="2-3-新建分页查询输入-DTO"><a href="#2-3-新建分页查询输入-DTO" class="headerlink" title="2.3 新建分页查询输入 DTO"></a>2.3 新建分页查询输入 DTO</h3><p>在 <code>Input</code> 目录下新建 <code>ModuleGetPageInput</code>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块分页请求</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">ModuleGetPageInput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="2-4-新建模块详情输出-DTO"><a href="#2-4-新建模块详情输出-DTO" class="headerlink" title="2.4 新建模块详情输出 DTO"></a>2.4 新建模块详情输出 DTO</h3><p>在 <code>Output</code> 目录下新建 <code>ModuleGetOutput</code>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Output</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块详情响应</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleGetOutput</span> : <span class="title">ModuleUpdateInput</span></span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="2-5-新建模块分页输出-DTO"><a href="#2-5-新建模块分页输出-DTO" class="headerlink" title="2.5 新建模块分页输出 DTO"></a>2.5 新建模块分页输出 DTO</h3><p>在 <code>Output</code> 目录下新建 <code>ModuleGetPageOutput</code>。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Output</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块分页响应</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleGetPageOutput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 主键</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">long</span> Id &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="3-新建模块服务接口"><a href="#3-新建模块服务接口" class="headerlink" title="3. 新建模块服务接口"></a>3. 新建模块服务接口</h2><p>在以下目录中新建 <code>Module</code> 目录：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api\Services</span><br></pre></td></tr></table></figure><p>然后在 <code>Module</code> 目录下新建模块服务类 <code>ModuleService</code>。</p><h3 id="动态接口生成说明"><a href="#动态接口生成说明" class="headerlink" title="动态接口生成说明"></a>动态接口生成说明</h3><p>若需要服务方法生成动态接口，需要在服务类上添加 <code>[DynamicApi(Area = ApiConsts.AreaName)]</code> 特性，并继承 <code>IDynamicApi</code> 接口。</p><blockquote><p>提示：</p><ul><li>使用项目模板开发时，请使用 <code>AppRepositoryBase</code> 基础仓储创建实体仓储。</li><li>在 <code>Admin.Core</code> 源码中开发时，请使用 <code>AdminRepositoryBase</code> 基础仓储创建实体仓储。</li></ul></blockquote><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Dto;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Services;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Contracts.Domain.Module;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Output;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Core.Consts;</span><br><span class="line"><span class="keyword">using</span> ZhonTai;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.DynamicApi;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.DynamicApi.Attributes;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Mvc;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Services.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块服务</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">DynamicApi(Area = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">BaseService</span>, <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> AppRepositoryBase&lt;ModuleEntity&gt; _moduleRep;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params">AppRepositoryBase&lt;ModuleEntity&gt; moduleRep</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleRep = moduleRep;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 查询模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;id&quot;&gt;</span>模块编号<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;returns&gt;</span>模块详情<span class="doctag">&lt;/returns&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> result = <span class="keyword">await</span> _moduleRep.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 查询分页</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;input&quot;&gt;</span>分页查询参数<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;returns&gt;</span>模块分页列表<span class="doctag">&lt;/returns&gt;</span></span></span><br><span class="line">    [<span class="meta">HttpPost</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">async</span> Task&lt;PageOutput&lt;ModuleGetPageOutput&gt;&gt; GetPageAsync(PageInput&lt;ModuleGetPageInput&gt; input)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> key = input.Filter?.Name;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> list = <span class="keyword">await</span> _moduleRep.Select</span><br><span class="line">            .WhereIf(key.NotNull(), a =&gt; a.Name.Contains(key))</span><br><span class="line">            .Count(<span class="keyword">out</span> <span class="keyword">var</span> total)</span><br><span class="line">            .OrderByDescending(<span class="literal">true</span>, c =&gt; c.Id)</span><br><span class="line">            .Page(input.CurrentPage, input.PageSize)</span><br><span class="line">            .ToListAsync&lt;ModuleGetPageOutput&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> data = <span class="keyword">new</span> PageOutput&lt;ModuleGetPageOutput&gt;</span><br><span class="line">        &#123;</span><br><span class="line">            List = list,</span><br><span class="line">            Total = total</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> data;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 新增模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;input&quot;&gt;</span>新增参数<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;returns&gt;</span>模块编号<span class="doctag">&lt;/returns&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;<span class="built_in">long</span>&gt; <span class="title">AddAsync</span>(<span class="params">ModuleAddInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> entity = Mapper.Map&lt;ModuleEntity&gt;(input);</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.InsertAsync(entity);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> entity.Id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 修改模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;input&quot;&gt;</span>修改参数<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">UpdateAsync</span>(<span class="params">ModuleUpdateInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> entity = <span class="keyword">await</span> _moduleRep.GetAsync(input.Id);</span><br><span class="line">        <span class="keyword">if</span> (!(entity?.Id &gt; <span class="number">0</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">throw</span> ResultOutput.Exception(<span class="string">&quot;模块不存在&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Mapper.Map(input, entity);</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.UpdateAsync(entity);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 彻底删除模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;id&quot;&gt;</span>模块编号<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">DeleteAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.DeleteAsync(m =&gt; m.Id == id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 软删除模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;id&quot;&gt;</span>模块编号<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">SoftDeleteAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.SoftDeleteAsync(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 批量软删除模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;ids&quot;&gt;</span>模块编号集合<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">BatchSoftDeleteAsync</span>(<span class="params"><span class="built_in">long</span>[] ids</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">await</span> _moduleRep.SoftDeleteAsync(ids);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="4-接口权限说明"><a href="#4-接口权限说明" class="headerlink" title="4. 接口权限说明"></a>4. 接口权限说明</h2><h3 id="4-1-Login"><a href="#4-1-Login" class="headerlink" title="4.1 [Login]"></a>4.1 <code>[Login]</code></h3><p>使用 <code>[Login]</code> 标记接口时，表示该接口需要用户登录后才能访问。</p><p>该标记常用于测试未授权但需要登录的接口。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Login</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">var</span> result = <span class="keyword">await</span> _moduleRep.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h3 id="4-2-AllowAnonymous"><a href="#4-2-AllowAnonymous" class="headerlink" title="4.2 [AllowAnonymous]"></a>4.2 <code>[AllowAnonymous]</code></h3><p>使用 <code>[AllowAnonymous]</code> 标记接口时，表示该接口允许匿名访问。</p><p>该标记常用于测试未授权且未登录的接口。</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">AllowAnonymous</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">var</span> result = <span class="keyword">await</span> _moduleRep.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="5-注意事项"><a href="#5-注意事项" class="headerlink" title="5. 注意事项"></a>5. 注意事项</h2><p>授权接口需要删除以下标记：</p><ul><li><code>[Login]</code></li><li><code>[AllowAnonymous]</code></li></ul><p>否则可能会影响接口权限控制逻辑。</p><p>#中台 #中台&#x2F;新建接口项目 #中台&#x2F;.NET模板 #中台&#x2F;搭建项目框架 #中台&#x2F;数据库配置</p>]]>
    </content>
    <id>https://zizai.cc/posts/new-module-interface/</id>
    <link href="https://zizai.cc/posts/new-module-interface/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>本文以 Module 模块为例，说明如何新建模块实体类、输入输出 DTO 以及模块服务接口。</summary>
    <title>新建模块接口</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>AYO-NET</name>
    </author>
    <category term="Admin Core" scheme="https://zizai.cc/categories/Admin-Core/"/>
    <category term="Admin Core" scheme="https://zizai.cc/tags/Admin-Core/"/>
    <category term="Asoka Core" scheme="https://zizai.cc/tags/Asoka-Core/"/>
    <category term="ZhonTai" scheme="https://zizai.cc/tags/ZhonTai/"/>
    <category term="仓储" scheme="https://zizai.cc/tags/%E4%BB%93%E5%82%A8/"/>
    <category term="模块" scheme="https://zizai.cc/tags/%E6%A8%A1%E5%9D%97/"/>
    <content>
      <![CDATA[<h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">MyCompanyName.MyProjectName.Api</span><br><span class="line">├── Domain</span><br><span class="line">│   └── Module</span><br><span class="line">│       └── ModuleEntity.cs</span><br><span class="line">├── Repositories</span><br><span class="line">│   └── Module</span><br><span class="line">│       └── ModuleRepository.cs</span><br><span class="line">├── Contracts</span><br><span class="line">│   └── Services</span><br><span class="line">│       └── Module</span><br><span class="line">│           ├── Input</span><br><span class="line">│           │   ├── ModuleAddInput.cs</span><br><span class="line">│           │   ├── ModuleUpdateInput.cs</span><br><span class="line">│           │   └── ModuleGetPageInput.cs</span><br><span class="line">│           └── Output</span><br><span class="line">│               ├── ModuleGetOutput.cs</span><br><span class="line">│               └── ModuleGetPageOutput.cs</span><br><span class="line">└── Services</span><br><span class="line">    └── Module</span><br><span class="line">        └── ModuleService.cs</span><br></pre></td></tr></table></figure><hr><h2 id="1-新建模块实体类"><a href="#1-新建模块实体类" class="headerlink" title="1. 新建模块实体类"></a>1. 新建模块实体类</h2><p><strong>路径</strong>: <code>Domain/Module/ModuleEntity.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Entities;</span><br><span class="line"><span class="keyword">using</span> FreeSql.DataAnnotations;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Domain.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块实体</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">Table(Name = ApiConsts.AreaName + <span class="string">&quot;_module&quot;</span>)</span>]</span><br><span class="line">[<span class="meta">Index(<span class="string">&quot;idx_&#123;tablename&#125;_01&quot;</span>, nameof(TenantId) + <span class="string">&quot;,&quot;</span> + nameof(Name), true)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">ModuleEntity</span> : <span class="title">EntityTenant</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">Column(StringLength = 50)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="约定说明"><a href="#约定说明" class="headerlink" title="约定说明"></a>约定说明</h3><table><thead><tr><th>约定项</th><th>说明</th></tr></thead><tbody><tr><td>实体类命名</td><td>以<code>Entity</code> 结尾，如 <code>ModuleEntity</code></td></tr><tr><td>表名命名</td><td>下划线命名法，前缀为项目前缀</td></tr><tr><td>索引命名</td><td>以<code>idx</code> 开头</td></tr><tr><td>多租户支持</td><td>继承<code>EntityTenant</code> 后自动按 <code>TenantId</code> 筛选数据</td></tr></tbody></table><hr><h2 id="2-新建模块仓储接口"><a href="#2-新建模块仓储接口" class="headerlink" title="2. 新建模块仓储接口"></a>2. 新建模块仓储接口</h2><p><strong>路径</strong>: <code>Domain/Module/IModuleRepository.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Repositories;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Domain.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块仓储接口</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">IModuleRepository</span> : <span class="title">IRepositoryBase</span>&lt;<span class="title">ModuleEntity</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="3-新建模块仓储实现"><a href="#3-新建模块仓储实现" class="headerlink" title="3. 新建模块仓储实现"></a>3. 新建模块仓储实现</h2><p><strong>路径</strong>: <code>Repositories/Module/ModuleRepository.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Core.Repositories;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Domain.Module;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Db.Transaction;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Repositories.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块仓储</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleRepository</span> : <span class="title">AppRepositoryBase</span>&lt;<span class="title">ModuleEntity</span>&gt;, <span class="title">IModuleRepository</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleRepository</span>(<span class="params">UnitOfWorkManagerCloud uowm</span>) : <span class="title">base</span>(<span class="params">uowm</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h3><blockquote><p>模块仓储需继承 <strong>App 项目基础仓储</strong> <code>AppRepositoryBase</code>，使用当前项目主库操作模块表。</p></blockquote><hr><h2 id="4-新建输入输出-Dto"><a href="#4-新建输入输出-Dto" class="headerlink" title="4. 新建输入输出 Dto"></a>4. 新建输入输出 Dto</h2><h3 id="4-1-添加输入-Dto"><a href="#4-1-添加输入-Dto" class="headerlink" title="4.1 添加输入 Dto"></a>4.1 添加输入 Dto</h3><p><strong>路径</strong>: <code>Contracts/Services/Module/Input/ModuleAddInput.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 添加模块</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleAddInput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-2-修改输入-Dto"><a href="#4-2-修改输入-Dto" class="headerlink" title="4.2 修改输入 Dto"></a>4.2 修改输入 Dto</h3><p><strong>路径</strong>: <code>Contracts/Services/Module/Input/ModuleUpdateInput.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.ComponentModel.DataAnnotations;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Validators;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 修改模块</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">ModuleUpdateInput</span> : <span class="title">ModuleAddInput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 编号</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    [<span class="meta">Required</span>]</span><br><span class="line">    [<span class="meta">ValidateRequired(<span class="string">&quot;请选择模块&quot;</span>)</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">long</span> Id &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-3-分页查询输入-Dto"><a href="#4-3-分页查询输入-Dto" class="headerlink" title="4.3 分页查询输入 Dto"></a>4.3 分页查询输入 Dto</h3><p><strong>路径</strong>: <code>Contracts/Services/Module/Input/ModuleGetPageInput.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块分页查询</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">partial</span> <span class="keyword">class</span> <span class="title">ModuleGetPageInput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-4-单条查询输出-Dto"><a href="#4-4-单条查询输出-Dto" class="headerlink" title="4.4 单条查询输出 Dto"></a>4.4 单条查询输出 Dto</h3><p><strong>路径</strong>: <code>Contracts/Services/Module/Output/ModuleGetOutput.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Input;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Output</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块详情</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleGetOutput</span> : <span class="title">ModuleUpdateInput</span></span><br><span class="line">&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-5-分页查询输出-Dto"><a href="#4-5-分页查询输出-Dto" class="headerlink" title="4.5 分页查询输出 Dto"></a>4.5 分页查询输出 Dto</h3><p><strong>路径</strong>: <code>Contracts/Services/Module/Output/ModuleGetPageOutput.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Contracts.Services.Module.Output</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块分页列表</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleGetPageOutput</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 主键</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">long</span> Id &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 名称</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> Name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="5-新建模块服务"><a href="#5-新建模块服务" class="headerlink" title="5. 新建模块服务"></a>5. 新建模块服务</h2><p><strong>路径</strong>: <code>Services/Module/ModuleService.cs</code></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> System.Threading.Tasks;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Core.Dto;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.Admin.Services;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Domain.Module;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Services.Module.Input;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Services.Module.Output;</span><br><span class="line"><span class="keyword">using</span> MyCompanyName.MyProjectName.Api.Core.Consts;</span><br><span class="line"><span class="keyword">using</span> ZhonTai;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.DynamicApi;</span><br><span class="line"><span class="keyword">using</span> ZhonTai.DynamicApi.Attributes;</span><br><span class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Mvc;</span><br><span class="line"><span class="keyword">using</span> Mapster;</span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> <span class="title">MyCompanyName.MyProjectName.Api.Services.Module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 模块服务</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">[<span class="meta">DynamicApi(Area = ApiConsts.AreaName)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ModuleService</span> : <span class="title">BaseService</span>, <span class="title">IDynamicApi</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">readonly</span> IModuleRepository _moduleRepository;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ModuleService</span>(<span class="params">IModuleRepository moduleRepository</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        _moduleRepository = moduleRepository;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 查询模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;id&quot;&gt;</span>模块编号<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;returns&gt;</span><span class="doctag">&lt;/returns&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> result = <span class="keyword">await</span> _moduleRepository.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 查询分页</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;input&quot;&gt;</span>分页参数<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;returns&gt;</span><span class="doctag">&lt;/returns&gt;</span></span></span><br><span class="line">    [<span class="meta">HttpPost</span>]</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">async</span> Task&lt;PageOutput&lt;ModuleGetPageOutput&gt;&gt; GetPageAsync(PageInput&lt;ModuleGetPageInput&gt; input)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> key = input.Filter?.Name;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">var</span> list = <span class="keyword">await</span> _moduleRepository.Select</span><br><span class="line">            .WhereIf(key.NotNull(), a =&gt; a.Name.Contains(key))</span><br><span class="line">            .Count(<span class="keyword">out</span> <span class="keyword">var</span> total)</span><br><span class="line">            .OrderByDescending(<span class="literal">true</span>, c =&gt; c.Id)</span><br><span class="line">            .Page(input.CurrentPage, input.PageSize)</span><br><span class="line">            .ToListAsync&lt;ModuleGetPageOutput&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> PageOutput&lt;ModuleGetPageOutput&gt;</span><br><span class="line">        &#123;</span><br><span class="line">            List = list,</span><br><span class="line">            Total = total</span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 新增模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;input&quot;&gt;</span>新增参数<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;returns&gt;</span>新模块编号<span class="doctag">&lt;/returns&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;<span class="built_in">long</span>&gt; <span class="title">AddAsync</span>(<span class="params">ModuleAddInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> entity = input.Adapt&lt;ModuleEntity&gt;();</span><br><span class="line">        <span class="keyword">await</span> _moduleRepository.InsertAsync(entity);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> entity.Id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 修改模块</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;input&quot;&gt;</span>修改参数<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">UpdateAsync</span>(<span class="params">ModuleUpdateInput input</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> entity = <span class="keyword">await</span> _moduleRepository.GetAsync(input.Id);</span><br><span class="line">        <span class="keyword">if</span> (entity?.Id == <span class="literal">null</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">throw</span> ResultOutput.Exception(<span class="string">&quot;模块不存在&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        input.Adapt(entity);</span><br><span class="line">        <span class="keyword">await</span> _moduleRepository.UpdateAsync(entity);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 彻底删除</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;id&quot;&gt;</span>模块编号<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">DeleteAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">await</span> _moduleRepository.DeleteAsync(m =&gt; m.Id == id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 软删除</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;id&quot;&gt;</span>模块编号<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">SoftDeleteAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">await</span> _moduleRepository.SoftDeleteAsync(id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> 批量软删除</span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line">    <span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;param name=&quot;ids&quot;&gt;</span>模块编号集合<span class="doctag">&lt;/param&gt;</span></span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task <span class="title">BatchSoftDeleteAsync</span>(<span class="params"><span class="built_in">long</span>[] ids</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">await</span> _moduleRepository.SoftDeleteAsync(ids);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="动态接口生成"><a href="#动态接口生成" class="headerlink" title="动态接口生成"></a>动态接口生成</h3><blockquote><p>在 <code>ModuleService</code> 上增加 <code>[DynamicApi(Area = ApiConsts.AreaName)]</code> 特性，并继承 <code>IDynamicApi</code> 接口。</p></blockquote><hr><h2 id="6-接口权限说明"><a href="#6-接口权限说明" class="headerlink" title="6. 接口权限说明"></a>6. 接口权限说明</h2><h3 id="6-1-需要登录"><a href="#6-1-需要登录" class="headerlink" title="6.1 需要登录"></a>6.1 需要登录</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">Login</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">var</span> result = <span class="keyword">await</span> _moduleRepository.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-2-允许匿名访问"><a href="#6-2-允许匿名访问" class="headerlink" title="6.2 允许匿名访问"></a>6.2 允许匿名访问</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[<span class="meta">AllowAnonymous</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ModuleGetOutput&gt; <span class="title">GetAsync</span>(<span class="params"><span class="built_in">long</span> id</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">var</span> result = <span class="keyword">await</span> _moduleRepository.GetAsync&lt;ModuleGetOutput&gt;(id);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="6-3-授权接口"><a href="#6-3-授权接口" class="headerlink" title="6.3 授权接口"></a>6.3 授权接口</h3><blockquote><p><strong>注意</strong>: 授权接口需删除 <code>[Login]</code> 和 <code>[AllowAnonymous]</code> 标记。</p></blockquote><hr><h2 id="命名替换参考"><a href="#命名替换参考" class="headerlink" title="命名替换参考"></a>命名替换参考</h2><table><thead><tr><th>占位符</th><th>替换为</th><th>示例</th></tr></thead><tbody><tr><td><code>MyCompanyName</code></td><td>公司名</td><td><code>ZhonTai</code></td></tr><tr><td><code>MyProjectName</code></td><td>项目名</td><td><code>Admin</code></td></tr><tr><td><code>Module</code></td><td>业务模块名</td><td><code>User</code>, <code>Role</code>, <code>Product</code></td></tr></tbody></table><p>#中台  |</p><p>#中台&#x2F;新建接口项目 #中台&#x2F;.NET模板 #中台&#x2F;分布式微服务 #仓储模式 #API接口</p>]]>
    </content>
    <id>https://zizai.cc/posts/new-module-repository/</id>
    <link href="https://zizai.cc/posts/new-module-repository/"/>
    <published>2026-06-13T03:20:00.000Z</published>
    <summary>路径: Domain/Module/ModuleEntity.cs</summary>
    <title>新建模块接口（仓储模式）</title>
    <updated>2026-06-13T03:20:00.000Z</updated>
  </entry>
</feed>
