docs: 重画系统架构图——单脊柱布局替代五线乌鸦脚,补 Redis 节点
Design Source Checks / design-source (push) Successful in 53s

客户端五端收进分组条(单一竖线入 nginx),标签白底不再压线,
nginx 泳道排满不留空带,MySQL/Redis 并排(jiu_redis 上线后架构图欠账)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
wangjia
2026-07-07 22:27:28 +08:00
parent fb8337b156
commit b20be72155
+63 -43
View File
@@ -69,55 +69,75 @@
<p class="gdesc">五端 Flutter 客户端统一访问 <code>https://jiu.51yanmei.com</code>。阿里云主机上 nginx 1.24 终结 TLS443 ssl+http2certbot webroot 自动续期,80 → 443 跳转),按 location 分发:API/健康检查/版本反代到本机 8081 的 Go 后端(systemd <code>jiu.service</code>),静态资源(Flutter Web <code>/app</code>、商品图 <code>/images</code>、安装包 <code>/downloads</code>、营销站根路径)直接由 nginx 服务;<code>/product/:id</code><code>/app/product/:id</code>(扫码分享页)回代后端注入 OG 标签。后端连本机 Docker 容器 <code>jiu_mysql</code>MySQL 8127.0.0.1:3306)。未鉴权的 <code>/api/v1/(public|auth)/</code> 在 nginx 层再加一道 per-IP 限流(10r/s burst 20)。源:<code>deploy/nginx-jiu-ali.conf</code><code>deploy/jiu.service</code></p>
<div class="svgwrap">
<svg viewBox="0 0 900 470" role="img" aria-label="系统架构图">
<svg viewBox="0 0 900 484" role="img" aria-label="系统架构图" font-family="-apple-system,'PingFang SC','Microsoft YaHei',sans-serif">
<defs>
<marker id="ar" markerWidth="8" markerHeight="8" refX="7" refY="3.5" orient="auto">
<path d="M0,0 L8,3.5 L0,7 Z" fill="#6E7888"/>
<marker id="ar" markerWidth="9" markerHeight="9" refX="7.5" refY="4" orient="auto">
<path d="M0,0.5 L8,4 L0,7.5 Z" fill="#2563AC"/>
</marker>
<linearGradient id="hd" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#F0F4FF"/><stop offset="1" stop-color="#E4EDFB"/>
</linearGradient>
</defs>
<!-- clients -->
<g font-size="12" text-anchor="middle">
<rect x="30" y="20" width="140" height="36" rx="6" fill="#F0F4FF" stroke="#2563AC"/><text x="100" y="43" fill="#154072">Windows</text>
<rect x="200" y="20" width="140" height="36" rx="6" fill="#F0F4FF" stroke="#2563AC"/><text x="270" y="43" fill="#154072">macOS</text>
<rect x="370" y="20" width="140" height="36" rx="6" fill="#F0F4FF" stroke="#2563AC"/><text x="440" y="43" fill="#154072">Web (/app)</text>
<rect x="540" y="20" width="140" height="36" rx="6" fill="#F0F4FF" stroke="#2563AC"/><text x="610" y="43" fill="#154072">Android</text>
<rect x="710" y="20" width="140" height="36" rx="6" fill="#F0F4FF" stroke="#2563AC"/><text x="780" y="43" fill="#154072">iOS</text>
<!-- 主干竖线(单一脊柱贯穿四层,替代旧版五线乌鸦脚) -->
<line x1="450" y1="86" x2="450" y2="116" stroke="#2563AC" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="450" y1="288" x2="450" y2="318" stroke="#2563AC" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="450" y1="384" x2="450" y2="414" stroke="#2563AC" stroke-width="1.6" marker-end="url(#ar)"/>
<g font-size="10.5" fill="#6E7888">
<rect x="463" y="92" width="152" height="16" rx="8" fill="#F5F7FA"/>
<text x="470" y="104">https://jiu.51yanmei.com</text>
<rect x="463" y="294" width="168" height="16" rx="8" fill="#F5F7FA"/>
<text x="470" y="306">proxy_pass 127.0.0.1:8081</text>
<rect x="463" y="390" width="120" height="16" rx="8" fill="#F5F7FA"/>
<text x="470" y="402">127.0.0.1:3306</text>
</g>
<line x1="100" y1="56" x2="430" y2="98" stroke="#6E7888" marker-end="url(#ar)"/>
<line x1="270" y1="56" x2="437" y2="98" stroke="#6E7888" marker-end="url(#ar)"/>
<line x1="440" y1="56" x2="443" y2="98" stroke="#6E7888" marker-end="url(#ar)"/>
<line x1="610" y1="56" x2="450" y2="98" stroke="#6E7888" marker-end="url(#ar)"/>
<line x1="780" y1="56" x2="457" y2="98" stroke="#6E7888" marker-end="url(#ar)"/>
<text x="450" y="88" text-anchor="middle" font-size="11" fill="#6E7888">https://jiu.51yanmei.com</text>
<!-- nginx -->
<rect x="30" y="100" width="840" height="170" rx="8" fill="#fff" stroke="#154072" stroke-width="1.5"/>
<text x="450" y="122" text-anchor="middle" font-size="13" font-weight="700" fill="#154072">ali · nginx 1.24 — 443 ssl+http2certbot webroot 自动续期)· 80 → 443</text>
<!-- 第 1 层:客户端分组条 -->
<rect x="30" y="18" width="840" height="68" rx="10" fill="#fff" stroke="#DCE2EB" stroke-width="1.2" stroke-dasharray="5 4"/>
<text x="46" y="38" font-size="11" font-weight="700" fill="#6E7888" letter-spacing="2">客户端 · FLUTTER 五端</text>
<g font-size="12" text-anchor="middle" font-weight="600">
<rect x="46" y="46" width="150" height="30" rx="15" fill="url(#hd)" stroke="#2563AC" stroke-width="1.1"/><text x="121" y="66" fill="#154072">Windows</text>
<rect x="212" y="46" width="150" height="30" rx="15" fill="url(#hd)" stroke="#2563AC" stroke-width="1.1"/><text x="287" y="66" fill="#154072">macOS</text>
<rect x="378" y="46" width="150" height="30" rx="15" fill="url(#hd)" stroke="#2563AC" stroke-width="1.1"/><text x="453" y="66" fill="#154072">Web/app</text>
<rect x="544" y="46" width="150" height="30" rx="15" fill="url(#hd)" stroke="#2563AC" stroke-width="1.1"/><text x="619" y="66" fill="#154072">Android</text>
<rect x="710" y="46" width="150" height="30" rx="15" fill="url(#hd)" stroke="#2563AC" stroke-width="1.1"/><text x="785" y="66" fill="#154072">iOS</text>
</g>
<!-- 第 2 层:nginx 入口 -->
<rect x="30" y="118" width="840" height="170" rx="10" fill="#fff" stroke="#154072" stroke-width="1.5"/>
<rect x="30" y="118" width="840" height="34" rx="10" fill="url(#hd)"/>
<rect x="30" y="142" width="840" height="10" fill="#fff"/>
<text x="450" y="140" text-anchor="middle" font-size="13" font-weight="700" fill="#154072">ali · nginx 1.24 — 443 ssl+http2certbot webroot 自动续期)· 80 → 443</text>
<g font-size="11" text-anchor="middle">
<rect x="50" y="136" width="190" height="52" rx="5" fill="#E6F3EC" stroke="#2E8B57"/>
<text x="145" y="157" fill="#2E8B57">静态:/appFlutter Web</text>
<text x="145" y="173" fill="#2E8B57">/images · /downloads · / 营销站</text>
<rect x="260" y="136" width="190" height="52" rx="5" fill="#F0F4FF" stroke="#2563AC"/>
<text x="355" y="157" fill="#154072">反代:/api /health /version</text>
<text x="355" y="173" fill="#154072">→ 127.0.0.1:8081</text>
<rect x="470" y="136" width="190" height="52" rx="5" fill="#FFF4E5" stroke="#B45309"/>
<text x="565" y="157" fill="#B45309">限流反代:/api/v1/(public|auth)</text>
<text x="565" y="173" fill="#B45309">10r/s · burst 20per-IP</text>
<rect x="680" y="136" width="170" height="52" rx="5" fill="#FDECEC" stroke="#8B2331"/>
<text x="765" y="157" fill="#8B2331">/product · /app/product</text>
<text x="765" y="173" fill="#8B2331">OG 注入 → 回代后端</text>
<text x="450" y="252" fill="#6E7888" font-size="11">证书 /etc/letsencrypt/live/jiu.51yanmei.com/ · client_max_body_size 20m · /import 超时 300s · 安全头 HSTS/XFO/nosniff</text>
<rect x="46" y="162" width="194" height="60" rx="7" fill="#E6F3EC" stroke="#2E8B57" stroke-width="1.1"/>
<text x="143" y="186" fill="#2E8B57" font-weight="700">静态直出</text>
<text x="143" y="203" fill="#2E8B57">/app · /images · /downloads · 官网</text>
<rect x="256" y="162" width="194" height="60" rx="7" fill="#F0F4FF" stroke="#2563AC" stroke-width="1.1"/>
<text x="353" y="186" fill="#154072" font-weight="700">反代 API</text>
<text x="353" y="203" fill="#154072">/api · /health · /version → :8081</text>
<rect x="466" y="162" width="194" height="60" rx="7" fill="#FFF4E5" stroke="#B45309" stroke-width="1.1"/>
<text x="563" y="186" fill="#B45309" font-weight="700">限流反代(per-IP</text>
<text x="563" y="203" fill="#B45309">/api/v1/(public|auth) 10r/s·burst20</text>
<rect x="676" y="162" width="178" height="60" rx="7" fill="#FDECEC" stroke="#8B2331" stroke-width="1.1"/>
<text x="765" y="186" fill="#8B2331" font-weight="700">公开页 SSR</text>
<text x="765" y="203" fill="#8B2331">/product · /shop → 回代后端</text>
</g>
<line x1="450" y1="270" x2="450" y2="308" stroke="#6E7888" marker-end="url(#ar)"/>
<text x="470" y="294" font-size="11" fill="#6E7888">proxy_pass 127.0.0.1:8081</text>
<!-- backend -->
<rect x="180" y="310" width="540" height="56" rx="8" fill="#fff" stroke="#2563AC" stroke-width="1.5"/>
<text x="450" y="333" text-anchor="middle" font-size="13" font-weight="700" fill="#154072">jiu-serverGo / Gin)— systemd jiu.service</text>
<text x="450" y="352" text-anchor="middle" font-size="11" fill="#6E7888">/opt/jiu/backend/jiu-server · EnvironmentFile=/opt/jiu/config/production.envSERVER_PORT=8081</text>
<line x1="450" y1="366" x2="450" y2="402" stroke="#6E7888" marker-end="url(#ar)"/>
<!-- mysql -->
<rect x="280" y="404" width="340" height="50" rx="8" fill="#fff" stroke="#2E8B57" stroke-width="1.5"/>
<text x="450" y="425" text-anchor="middle" font-size="13" font-weight="700" fill="#2E8B57">MySQL 8 — docker 容器 jiu_mysql</text>
<text x="450" y="443" text-anchor="middle" font-size="11" fill="#6E7888">127.0.0.1:3306 · 库 jiu_db · 每日备份见 §6</text>
<line x1="46" y1="240" x2="854" y2="240" stroke="#ECEFF4"/>
<text x="450" y="264" text-anchor="middle" fill="#99A3B3" font-size="10.5">证书 /etc/letsencrypt/live/jiu.51yanmei.com/ · client_max_body_size 20m · /import 超时 300s · 安全头 HSTS / XFO / nosniff</text>
<!-- 第 3 层:Go 后端 -->
<rect x="180" y="320" width="540" height="64" rx="10" fill="#fff" stroke="#2563AC" stroke-width="1.5"/>
<text x="450" y="345" text-anchor="middle" font-size="13" font-weight="700" fill="#154072">jiu-serverGo / Gin)— systemd jiu.service · :8081</text>
<text x="450" y="366" text-anchor="middle" font-size="10.5" fill="#6E7888">/opt/jiu/backend/jiu-server · EnvironmentFile=/opt/jiu/config/production.env</text>
<!-- 第 4 层:MySQL + Redis 并排 -->
<rect x="180" y="416" width="330" height="56" rx="10" fill="#fff" stroke="#2E8B57" stroke-width="1.5"/>
<text x="345" y="439" text-anchor="middle" font-size="12.5" font-weight="700" fill="#2E8B57">MySQL 8 — docker jiu_mysql</text>
<text x="345" y="458" text-anchor="middle" font-size="10.5" fill="#6E7888">127.0.0.1:3306 · 库 jiu_db · 每日备份见 §6</text>
<rect x="530" y="416" width="190" height="56" rx="10" fill="#fff" stroke="#B45309" stroke-width="1.2"/>
<text x="625" y="439" text-anchor="middle" font-size="12.5" font-weight="700" fill="#B45309">Redis 7 — jiu_redis</text>
<text x="625" y="458" text-anchor="middle" font-size="10.5" fill="#6E7888">限流/登录锁 · :6379</text>
<path d="M 625 384 L 625 414" stroke="#B45309" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#ar)"/>
</svg>
</div>