fix(subscribe): improve query logic and handle status filtering

This commit is contained in:
Chang lue Tsen 2025-04-29 12:46:19 +09:00
parent fd1104784b
commit de74bd1d70
3 changed files with 859 additions and 2 deletions

View File

@ -3,7 +3,7 @@
<div align="center">
[![License](https://img.shields.io/github/license/perfect-panel/server)](LICENSE)
![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue)
[![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue)](https://go.dev/)
[![Go Report Card](https://goreportcard.com/badge/github.com/perfect-panel/server)](https://goreportcard.com/report/github.com/perfect-panel/server)
[![Docker](https://img.shields.io/badge/Docker-Available-blue)](Dockerfile)
[![CI/CD](https://img.shields.io/github/actions/workflow/status/perfect-panel/server/release.yml)](.github/workflows/release.yml)
@ -125,6 +125,10 @@ The documentation covers all endpoints, request/response formats, and authentica
Visit [ppanel.dev](https://ppanel.dev/) for more details.
## 🏛 Architecture
![Architecture Diagram](./doc/image/architecture.svg)
## 📁 Directory Structure
```

849
doc/image/architecture.svg Normal file
View File

@ -0,0 +1,849 @@
<svg aria-roledescription="flowchart-v2" role="graphics-document document"
viewBox="0 0 1521.3046875 1152" style="max-width: 3840px; background-color: white; max-height: 3840px;"
class="flowchart" xmlns="http://www.w3.org/2000/svg" width="100%" id="my-svg">
<style>#my-svg{font-family:arial,sans-serif;font-size:14px;fill:#333;}@keyframes
edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#my-svg
.edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear
infinite;stroke-linecap:round;}#my-svg
.edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear
infinite;stroke-linecap:round;}#my-svg .error-icon{fill:#ffffff;}#my-svg
.error-text{fill:#000000;stroke:#000000;}#my-svg .edge-thickness-normal{stroke-width:2px;}#my-svg
.edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg
.edge-thickness-invisible{stroke-width:0;fill:none;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg
.edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#000000;stroke:#000000;}#my-svg
.marker.cross{stroke:#000000;}#my-svg svg{font-family:arial,sans-serif;font-size:14px;}#my-svg
p{margin:0;}#my-svg .label{font-family:arial,sans-serif;color:#333;}#my-svg .cluster-label
text{fill:#000000;}#my-svg .cluster-label span{color:#000000;}#my-svg .cluster-label span
p{background-color:transparent;}#my-svg .label text,#my-svg span{fill:#333;color:#333;}#my-svg .node
rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node
path{fill:#ffffff;stroke:#000000;stroke-width:2px;}#my-svg .rough-node .label text,#my-svg .node .label
text,#my-svg .image-shape .label,#my-svg .icon-shape .label{text-anchor:middle;}#my-svg .node .katex
path{fill:#000;stroke:#000;stroke-width:1px;}#my-svg .rough-node .label,#my-svg .node .label,#my-svg
.image-shape .label,#my-svg .icon-shape .label{text-align:center;}#my-svg
.node.clickable{cursor:pointer;}#my-svg .root .anchor
path{fill:#000000!important;stroke-width:0;stroke:#000000;}#my-svg .arrowheadPath{fill:#000000;}#my-svg
.edgePath .path{stroke:#000000;stroke-width:2px;}#my-svg .flowchart-link{stroke:#000000;fill:none;}#my-svg
.edgeLabel{background-color:hsl(-120, 0%, 80%);text-align:center;}#my-svg .edgeLabel
p{background-color:hsl(-120, 0%, 80%);}#my-svg .edgeLabel rect{opacity:0.5;background-color:hsl(-120, 0%,
80%);fill:hsl(-120, 0%, 80%);}#my-svg .labelBkg{background-color:rgba(204, 204, 204, 0.5);}#my-svg .cluster
rect{fill:#ffffff;stroke:hsl(0, 0%, 90%);stroke-width:2px;}#my-svg .cluster text{fill:#000000;}#my-svg .cluster
span{color:#000000;}#my-svg
div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:#ffffff;border:1px
solid hsl(0, 0%, 90%);border-radius:2px;pointer-events:none;z-index:100;}#my-svg
.flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#my-svg
rect.text{fill:none;stroke-width:0;}#my-svg .icon-shape,#my-svg .image-shape{background-color:hsl(-120, 0%,
80%);text-align:center;}#my-svg .icon-shape p,#my-svg .image-shape p{background-color:hsl(-120, 0%,
80%);padding:2px;}#my-svg .icon-shape rect,#my-svg .image-shape rect{opacity:0.5;background-color:hsl(-120, 0%,
80%);fill:hsl(-120, 0%, 80%);}#my-svg .node .neo-node{stroke:#000000;}#my-svg [data-look="neo"].node
rect,#my-svg [data-look="neo"].cluster rect,#my-svg [data-look="neo"].node
polygon{stroke:url(#my-svg-gradient);filter:drop-shadow( 0px 1px 2px rgba(0, 0, 0, 0.25));}#my-svg
[data-look="neo"].node path{stroke:url(#my-svg-gradient);stroke-width:2;}#my-svg [data-look="neo"].node
.outer-path{filter:drop-shadow( 0px 1px 2px rgba(0, 0, 0, 0.25));}#my-svg [data-look="neo"].node .neo-line
path{stroke:#000000;filter:none;}#my-svg [data-look="neo"].node
circle{stroke:url(#my-svg-gradient);filter:drop-shadow( 0px 1px 2px rgba(0, 0, 0, 0.25));}#my-svg
[data-look="neo"].node circle .state-start{fill:#000000;}#my-svg [data-look="neo"].statediagram-cluster
rect{fill:#ffffff;stroke:url(#my-svg-gradient);stroke-width:2;}#my-svg [data-look="neo"].icon-shape
.icon{fill:url(#my-svg-gradient);filter:drop-shadow( 0px 1px 2px rgba(0, 0, 0, 0.25));}#my-svg
[data-look="neo"].icon-shape .icon-neo path{stroke:url(#my-svg-gradient);filter:drop-shadow( 0px 1px 2px rgba(0,
0, 0, 0.25));}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
</style>
<g>
<marker orient="auto" markerHeight="14" markerWidth="10.5" markerUnits="userSpaceOnUse" refY="7" refX="7.75"
viewBox="0 0 11.5 14" class="marker flowchart-v2" id="my-svg_flowchart-v2-pointEnd">
<path style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 11.5 7 L 0 14 z"/>
</marker>
<marker orient="auto" markerHeight="14" markerWidth="11.5" markerUnits="userSpaceOnUse" refY="7" refX="4"
viewBox="0 0 11.5 14" class="marker flowchart-v2" id="my-svg_flowchart-v2-pointStart">
<polygon style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath"
points="0,7 11.5,14 11.5,0"/>
</marker>
<marker orient="auto" markerHeight="14" markerWidth="10.5" markerUnits="userSpaceOnUse" refY="7" refX="11.5"
viewBox="0 0 11.5 14" class="marker flowchart-v2" id="my-svg_flowchart-v2-pointEnd-margin">
<path style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath" d="M 0 0 L 11.5 7 L 0 14 z"/>
</marker>
<marker orient="auto" markerHeight="14" markerWidth="11.5" markerUnits="userSpaceOnUse" refY="7" refX="1"
viewBox="0 0 11.5 14" class="marker flowchart-v2" id="my-svg_flowchart-v2-pointStart-margin">
<polygon style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath"
points="0,7 11.5,14 11.5,0"/>
</marker>
<marker orient="auto" markerHeight="14" markerWidth="14" markerUnits="userSpaceOnUse" refX="10.75" refY="5"
viewBox="0 0 10 10" class="marker flowchart-v2" id="my-svg_flowchart-v2-circleEnd">
<circle style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"/>
</marker>
<marker orient="auto" markerHeight="14" markerWidth="14" markerUnits="userSpaceOnUse" refY="5" refX="0"
viewBox="0 0 10 10" class="marker flowchart-v2" id="my-svg_flowchart-v2-circleStart">
<circle style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"/>
</marker>
<marker orient="auto" markerHeight="14" markerWidth="14" markerUnits="userSpaceOnUse" refX="12.25" refY="5"
viewBox="0 0 10 10" class="marker flowchart-v2" id="my-svg_flowchart-v2-circleEnd-margin">
<circle style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"/>
</marker>
<marker orient="auto" markerHeight="14" markerWidth="14" markerUnits="userSpaceOnUse" refY="5" refX="-2"
viewBox="0 0 10 10" class="marker flowchart-v2" id="my-svg_flowchart-v2-circleStart-margin">
<circle style="stroke-width: 0; stroke-dasharray: 1, 0;" class="arrowMarkerPath" r="5" cy="5" cx="5"/>
</marker>
<marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="7.5" refX="17.7"
viewBox="0 0 15 15" class="marker cross flowchart-v2" id="my-svg_flowchart-v2-crossEnd">
<path style="stroke-width: 2.5;" class="arrowMarkerPath" d="M 1,1 L 14,14 M 1,14 L 14,1"/>
</marker>
<marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="7.5" refX="-3.5"
viewBox="0 0 15 15" class="marker cross flowchart-v2" id="my-svg_flowchart-v2-crossStart">
<path style="stroke-width: 2.5; stroke-dasharray: 1, 0;" class="arrowMarkerPath"
d="M 1,1 L 14,14 M 1,14 L 14,1"/>
</marker>
<marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="7.5" refX="17.7"
viewBox="0 0 15 15" class="marker cross flowchart-v2" id="my-svg_flowchart-v2-crossEnd-margin">
<path style="stroke-width: 2.5;" class="arrowMarkerPath" d="M 1,1 L 14,14 M 1,14 L 14,1"/>
</marker>
<marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="7.5" refX="-3.5"
viewBox="0 0 15 15" class="marker cross flowchart-v2" id="my-svg_flowchart-v2-crossStart-margin">
<path style="stroke-width: 2.5; stroke-dasharray: 1, 0;" class="arrowMarkerPath"
d="M 1,1 L 14,14 M 1,14 L 14,1"/>
</marker>
<g class="root">
<g class="clusters">
<g data-look="neo" data-et="cluster" data-id="部署环境" id="部署环境" class="cluster">
<rect height="116" width="268.3828125" y="145" x="351.8359375" style="fill:#ffffff"/>
<g transform="translate(458.02734375, 145)" class="cluster-label">
<foreignObject height="21" width="56">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>部署环境</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g data-look="neo" data-et="cluster" data-id="perfect-panel/server" id="perfect-panel/server"
class="cluster">
<rect height="580" width="1162.3046875" y="332" x="84" style="fill:#ffffff"/>
<g transform="translate(602.89453125, 332)" class="cluster-label">
<foreignObject height="21" width="124.515625">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>perfect-panel/server</p>
</span>
</div>
</foreignObject>
</g>
</g>
</g>
<g class="edgePaths">
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTIyNi4zMDQ2ODc1LCJ5Ijo2My41fSx7IngiOjEyMjYuMzA0Njg3NSwieSI6MTA5LjV9LHsieCI6MTIyNi4zMDQ2ODc1LCJ5IjoxNDV9LHsieCI6MTIyNi4zMDQ2ODc1LCJ5IjoxODAuNX1d"
data-id="L_A_B_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 104 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_A_B_0" d="M1226.3046875,63.5L1226.3046875,109.5L1226.3046875,145L1226.3046875,176.5"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTAwOS4zMDQ2ODc1LCJ5Ijo3NH0seyJ4IjoxMDA5LjMwNDY4NzUsInkiOjEwOS41fSx7IngiOjEwMDkuMzA0Njg3NSwieSI6MTQ1fSx7IngiOjEwMDkuMzA0Njg3NSwieSI6MTgwLjV9XQ=="
data-id="L_C_D_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 93.5 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_C_D_0" d="M1009.3046875,74L1009.3046875,109.5L1009.3046875,145L1009.3046875,176.5"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6NTkzLjI4NTE1NjI1LCJ5Ijo0MDJ9LHsieCI6NTkzLjI4NTE1NjI1LCJ5Ijo0Mjd9LHsieCI6NTkzLjI4NTE1NjI1LCJ5Ijo0NTJ9XQ=="
data-id="L_E_F_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 37 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_E_F_0" d="M593.28515625,402L593.28515625,427L593.28515625,448"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6NDkwLjkxMDE1NjI1LCJ5Ijo0OTEuMDcyNjkzMjc2MzQ4OX0seyJ4IjoyMzUsInkiOjUzMi41fSx7IngiOjIzNSwieSI6NTY4fV0="
data-id="L_F_G_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 279.3564758300781 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_F_G_0"
d="M490.91015625,491.0726932763489L242.61512277672801,531.2672469446599Q235,532.5 235,540.214257903395L235,564"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6NjgzLjEwOTM3NSwieSI6NDk3fSx7IngiOjgyNC44MzIwMzEyNSwieSI6NTMyLjV9LHsieCI6ODI0LjgzMjAzMTI1LCJ5Ijo1NzguNX1d"
data-id="L_F_H_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 176.86302185058594 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_F_H_0"
d="M683.109375,497L816.9485603840262,530.5252753995212Q824.83203125,532.5 824.83203125,540.6270320623456L824.83203125,574.5"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6ODI0LjgzMjAzMTI1LCJ5Ijo2MjMuNX0seyJ4Ijo4MjQuODMyMDMxMjUsInkiOjY1OX0seyJ4Ijo4NTYuNDIwNDYwNjY4MTAzNSwieSI6Njg0fV0="
data-id="L_H_I_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 61.272499084472656 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_H_I_0"
d="M824.83203125,623.5L824.83203125,647.5203223455075Q824.83203125,659 833.8336655149276,666.1241546594341L853.2839146653837,681.5176464448385"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6NzgyLjExNzE4NzUsInkiOjcyOC42MDEzMjMxNDM2MDY3fSx7IngiOjIxMy4xOTUzMTI1LCJ5Ijo3ODUuNX0seyJ4IjoyMTMuMTk1MzEyNSwieSI6ODMxLjV9XQ=="
data-id="L_I_J_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 602.2693481445312 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_I_J_0"
d="M782.1171875,728.6013231436067L220.60987572526903,784.758459103922Q213.1953125,785.5 213.1953125,792.9515522357472L213.1953125,827.5"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6NzgyLjExNzE4NzUsInkiOjczNS4wNzgzNTA1MTU0NjR9LHsieCI6NDU4LjU4NTkzNzUsInkiOjc4NS41fSx7IngiOjQ1OC41ODU5Mzc1LCJ5Ijo4MzEuNX1d"
data-id="L_I_K_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 358.0413818359375 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_I_K_0"
d="M782.1171875,735.078350515464L466.18195285051667,784.3161773445906Q458.5859375,785.5 458.5859375,793.1877100156513L458.5859375,827.5"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6ODE1LjA5NDAzNTEyNzczNzIsInkiOjc1MH0seyJ4Ijo3MjUuNzgxMjUsInkiOjc4NS41fSx7IngiOjcyNS43ODEyNSwieSI6ODIxfV0="
data-id="L_I_L_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 116.60028076171875 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_I_L_0"
d="M815.0940351277372,750L734.055821292799,782.2110276487936Q725.78125,785.5 725.78125,794.4042612948303L725.78125,817"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6OTU5LjIzMTkyMjkwMTQ1OTgsInkiOjc1MH0seyJ4IjoxMDI0Ljk3NjU2MjUsInkiOjc4NS41fSx7IngiOjEwMjQuOTc2NTYyNSwieSI6ODMxLjV9XQ=="
data-id="L_I_M_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 105.9078140258789 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_I_M_0"
d="M959.2319229014598,750L1016.3884104923951,780.8626729398522Q1024.9765625,785.5 1024.9765625,795.2601822302919L1024.9765625,827.5"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTI0LCJ5IjoyMzZ9LHsieCI6MTI0LCJ5IjoyNjF9LHsieCI6MTI0LCJ5IjoyOTYuNX0seyJ4IjoxMjQsInkiOjMzMn0seyJ4Ijo1MjUuMjg1MTU2MjUsInkiOjM3Mi42MTcxOTEyMDY3MDU3fV0="
data-id="L_N_E_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 483.8468933105469 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_N_E_0"
d="M124,236L124,261L124,296.5L124,324.5435263582809Q124,332 131.4185687185192,332.7508910295512L521.305490217514,372.2143782601944"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTI0LCJ5Ijo2My41fSx7IngiOjEyNCwieSI6MTA5LjV9LHsieCI6MTI0LCJ5IjoxNDV9LHsieCI6MTI0LCJ5IjoxNzB9XQ=="
data-id="L_O_N_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 93.5 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_O_N_0" d="M124,63.5L124,109.5L124,145L124,166"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTI4MS4zMDQ2ODc1LCJ5Ijo2MjkuNDQ4MjAyOTU5ODMwOH0seyJ4IjoxMTYwLjgwNDY4NzUsInkiOjY1OX0seyJ4IjoxMDE0LjExNzE4NzUsInkiOjY5MS4zODc4MTgyMjUwNzc0fV0="
data-id="L_P_I_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 261.2856750488281 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_P_I_0"
d="M1281.3046875,629.4482029598308L1221.0546875,644.2241014799154Q1160.8046875,659 1100.228285514959,672.3749467147557L1018.0231129065415,690.5254107120461"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTAyNC45NzY1NjI1LCJ5Ijo4NzYuNX0seyJ4IjoxMDI0Ljk3NjU2MjUsInkiOjkxMn0seyJ4IjoxMDI0Ljk3NjU2MjUsInkiOjk0Ny41fSx7IngiOjEwMjQuOTc2NTYyNSwieSI6OTgzfV0="
data-id="L_M_Q_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 93.5 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_M_Q_0" d="M1024.9765625,876.5L1024.9765625,912L1024.9765625,947.5L1024.9765625,979"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6ODk4LjExNzE4NzUsInkiOjc1MH0seyJ4Ijo4OTguMTE3MTg3NSwieSI6Nzg1LjV9LHsieCI6ODk4LjExNzE4NzUsInkiOjg1NH0seyJ4Ijo4OTguMTE3MTg3NSwieSI6OTEyfSx7IngiOjg5OC4xMTcxODc1LCJ5Ijo5NDcuNX0seyJ4Ijo4OTguMTE3MTg3NSwieSI6MTAwNS41fSx7IngiOjg5OC4xMTcxODc1LCJ5IjoxMDYzLjV9LHsieCI6OTg0LjczNDM3NSwieSI6MTA5OS43OTc0NzEyMTI0NjMzfV0="
data-id="L_I_R_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 392.437744140625 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_I_R_0"
d="M898.1171875,750L898.1171875,785.5L898.1171875,854L898.1171875,912L898.1171875,947.5L898.1171875,1005.5L898.1171875,1054.4723431458365Q898.1171875,1063.5 906.4433283841482,1066.989121129144L981.045204700928,1098.2515014506484"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTAxNC4xMTcxODc1LCJ5Ijo3NDMuNDAyNzgyODI1Mzk4NX0seyJ4IjoxMTk5LjA3MDMxMjUsInkiOjc4NS41fSx7IngiOjEzMTguODM5MjQ0OTgxNzUxOCwieSI6ODMxLjV9XQ=="
data-id="L_I_S_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 304.7711181640625 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_I_S_0"
d="M1014.1171875,743.4027828253985L1138.4707845644596,771.7069295223815Q1199.0703125,785.5 1257.0877483785657,807.7829242535049L1315.1051842571312,830.0658485070098"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6NDczLjA3NDUyMTgyMTEyMDcsInkiOjIyNS41fSx7IngiOjQ3MC41MjM0Mzc1LCJ5IjoyNjF9LHsieCI6NDcwLjUyMzQzNzUsInkiOjI5Ni41fSx7IngiOjQ3MC41MjM0Mzc1LCJ5IjozMzJ9LHsieCI6NTM1LjEzNDg2ODQyMTA1MjYsInkiOjM1N31d"
data-id="L_T_E_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 160.83079528808594 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_T_E_0"
d="M473.0745218211207,225.5L471.7956988612642,243.29565445918337Q470.5234375,261 470.5234375,278.75L470.5234375,296.5L470.5234375,323.155237512116Q470.5234375,332 478.7722482119,335.19169943859436L531.4043846581704,355.5565697471395"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6NTIzLjM4NzM1ODU2NjgxMDMsInkiOjIyNS41fSx7IngiOjYwMC4yMTg3NSwieSI6MjYxfSx7IngiOjYwMC4yMTg3NSwieSI6Mjk2LjV9LHsieCI6NjAwLjIxODc1LCJ5IjozMzJ9LHsieCI6NTk2LjU2OTQ5MDEzMTU3OSwieSI6MzU3fV0="
data-id="L_T_E_2" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 165.94847106933594 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_T_E_2"
d="M523.3873585668103,225.5L591.7942574335991,257.1074571144882Q600.21875,261 600.21875,270.2802998398162L600.21875,296.5L600.21875,321.3675309461366Q600.21875,332 598.6829994489774,342.52097278897713L597.1472488979549,353.04194557795427"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTIyNi4zMDQ2ODc1LCJ5IjoyMjUuNX0seyJ4IjoxMjI2LjMwNDY4NzUsInkiOjI2MX0seyJ4IjoxMjI2LjMwNDY4NzUsInkiOjI5Ni41fSx7IngiOjEyMjYuMzA0Njg3NSwieSI6MzMyfSx7IngiOjY2MS4yODUxNTYyNSwieSI6Mzc0LjM5NzQ3MTgxNDc3NjZ9XQ=="
data-id="L_B_E_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 657.5743408203125 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_B_E_0"
d="M1226.3046875,225.5L1226.3046875,261L1226.3046875,296.5L1226.3046875,324.64854193959343Q1226.3046875,332 1218.9738389277861,332.5500861979607L665.2739424265525,374.0981645516783"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTAyNC45NzY1NjI1LCJ5IjoxMDI4fSx7IngiOjEwMjQuOTc2NTYyNSwieSI6MTA2My41fSx7IngiOjEwMzIuMDQ0MDQ2MzM2MjA3LCJ5IjoxMDk5fV0="
data-id="L_Q_R_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 58.59327697753906 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_Q_R_0"
d="M1024.9765625,1028L1024.9765625,1047.4016621773224Q1024.9765625,1063.5 1028.1197998366633,1079.2884939021574L1031.2630371733267,1095.0769878043145"/>
<path marker-end="url(#my-svg_flowchart-v2-pointEnd-margin)"
data-points="W3sieCI6MTA4MS4yNTY3MzQ5MTM3OTMsInkiOjEwOTl9LHsieCI6MTE1MS44MzU5Mzc1LCJ5IjoxMDYzLjV9LHsieCI6MTE1MS44MzU5Mzc1LCJ5IjoxMDA1LjV9LHsieCI6MTE1MS44MzU5Mzc1LCJ5Ijo5NDcuNX0seyJ4IjoxMTUxLjgzNTkzNzUsInkiOjkxMn0seyJ4IjoxMTUxLjgzNTkzNzUsInkiOjg1NH0seyJ4IjoxMTUxLjgzNTkzNzUsInkiOjc4NS41fSx7IngiOjEwMTQuMTE3MTg3NSwieSI6NzQ4LjMxODE0MjYyODQwMjV9XQ=="
data-id="L_R_I_0" data-et="edge" data-edge="true"
style="stroke-dasharray: 0 0 482.588623046875 9; stroke-dashoffset: 0;;"
class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link"
id="L_R_I_0"
d="M1081.256734913793,1099L1143.3231740485703,1067.7817585273315Q1151.8359375,1063.5 1151.8359375,1053.9710652396834L1151.8359375,1005.5L1151.8359375,947.5L1151.8359375,912L1151.8359375,854L1151.8359375,793.7235698902426Q1151.8359375,785.5 1143.8966319753904,783.356514631119L1017.9789193904433,749.3607484054503"/>
</g>
<g class="edgeLabels">
<g transform="translate(1226.3046875, 109.5)" class="edgeLabel">
<g transform="translate(-22.9453125, -10.5)" data-id="L_A_B_0" class="label">
<foreignObject height="21" width="45.890625">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>HTTPS</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1009.3046875, 109.5)" class="edgeLabel">
<g transform="translate(-68.46875, -10.5)" data-id="L_C_D_0" class="label">
<foreignObject height="21" width="136.9375">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>代理协议: SS, Trojan..</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_E_F_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(235, 532.5)" class="edgeLabel">
<g transform="translate(-21, -10.5)" data-id="L_F_G_0" class="label">
<foreignObject height="21" width="42">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>中间件</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_F_H_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_H_I_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_I_J_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_I_K_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_I_L_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_I_M_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_N_E_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_O_N_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g class="edgeLabel">
<g transform="translate(0, 0)" data-id="L_P_I_0" class="label">
<foreignObject height="0" width="0">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel"></span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1024.9765625, 947.5)" class="edgeLabel">
<g transform="translate(-14, -10.5)" data-id="L_M_Q_0" class="label">
<foreignObject height="21" width="28">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>读写</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(898.1171875, 947.5)" class="edgeLabel">
<g transform="translate(-14, -10.5)" data-id="L_I_R_0" class="label">
<foreignObject height="21" width="28">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>缓存</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1199.0703125, 785.5)" class="edgeLabel">
<g transform="translate(-27.234375, -10.5)" data-id="L_I_S_0" class="label">
<foreignObject height="21" width="54.46875">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>API 调用</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(470.5234375, 296.5)" class="edgeLabel">
<g transform="translate(-78.5703125, -10.5)" data-id="L_T_E_0" class="label">
<foreignObject height="21" width="157.140625">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>构建脚本: Makefile, script</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(600.21875, 296.5)" class="edgeLabel">
<g transform="translate(-31.125, -10.5)" data-id="L_T_E_2" class="label">
<foreignObject height="21" width="62.25">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>Dockerfile</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1226.3046875, 296.5)" class="edgeLabel">
<g transform="translate(-38.90625, -10.5)" data-id="L_B_E_0" class="label">
<foreignObject height="21" width="77.8125">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>RESTful API</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1024.9765625, 1063.5)" class="edgeLabel">
<g transform="translate(-35, -10.5)" data-id="L_Q_R_0" class="label">
<foreignObject height="21" width="70">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>持久化数据</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1151.8359375, 947.5)" class="edgeLabel">
<g transform="translate(-28, -10.5)" data-id="L_R_I_0" class="label">
<foreignObject height="21" width="56">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
class="labelBkg" xmlns="http://www.w3.org/1999/xhtml">
<span class="edgeLabel">
<p>缓存数据</p>
</span>
</div>
</foreignObject>
</g>
</g>
</g>
<g class="nodes">
<g transform="translate(1226.3046875, 41)" data-look="neo" data-et="node" data-node="true" data-id="A"
id="flowchart-A-0" class="node default">
<rect stroke="url(#gradient)" height="45" width="102" y="-22.5" x="-51" data-id="A" style=""
class="basic label-container"/>
<g transform="translate(-35, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="70">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>用户浏览器</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1226.3046875, 203)" data-look="neo" data-et="node" data-node="true" data-id="B"
id="flowchart-B-1" class="node default">
<rect stroke="url(#gradient)" height="45" width="140.171875" y="-22.5" x="-70.0859375" data-id="B"
style="" class="basic label-container"/>
<g transform="translate(-54.0859375, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="108.171875">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>前端: ppanel-web</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1009.3046875, 41)" data-look="neo" data-et="node" data-node="true" data-id="C"
id="flowchart-C-2" class="node default">
<rect stroke="url(#gradient)" height="66" width="232" y="-33" x="-116" data-id="C" style=""
class="basic label-container"/>
<g transform="translate(-100, -21)" style="" class="label">
<rect/>
<foreignObject height="42" width="200">
<div style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>代理客户端: Clash,Shadowrocket</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1009.3046875, 203)" data-look="neo" data-et="node" data-node="true" data-id="D"
id="flowchart-D-3" class="node default">
<rect stroke="url(#gradient)" height="45" width="168.140625" y="-22.5" x="-84.0703125" data-id="D"
style="" class="basic label-container"/>
<g transform="translate(-68.0703125, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="136.140625">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>代理核心: Xray-core...</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(593.28515625, 379.5)" data-look="neo" data-et="node" data-node="true"
data-id="E" id="flowchart-E-4" class="node default">
<rect stroke="url(#gradient)" height="45" width="136" y="-22.5" x="-68" data-id="E" style=""
class="basic label-container"/>
<g transform="translate(-52, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="104">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>Web 服务器: Gin</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(593.28515625, 474.5)" data-look="neo" data-et="node" data-node="true"
data-id="F" id="flowchart-F-5" class="node default">
<rect stroke="url(#gradient)" height="45" width="204.75" y="-22.5" x="-102.375" data-id="F" style=""
class="basic label-container"/>
<g transform="translate(-86.375, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="172.75">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>API 控制器: internal/handler</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(235, 601)" data-look="neo" data-et="node" data-node="true" data-id="G"
id="flowchart-G-7" class="node default">
<rect stroke="url(#gradient)" height="66" width="232" y="-33" x="-116" data-id="G" style=""
class="basic label-container"/>
<g transform="translate(-100, -21)" style="" class="label">
<rect/>
<foreignObject height="42" width="200">
<div style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>中间件: internal/middleware\n认证、日志、CORS</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(824.83203125, 601)" data-look="neo" data-et="node" data-node="true" data-id="H"
id="flowchart-H-9" class="node default">
<rect stroke="url(#gradient)" height="45" width="152.59375" y="-22.5" x="-76.296875" data-id="H"
style="" class="basic label-container"/>
<g transform="translate(-60.296875, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="120.59375">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>服务层: internal/svc</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(898.1171875, 717)" data-look="neo" data-et="node" data-node="true" data-id="I"
id="flowchart-I-11" class="node default">
<rect stroke="url(#gradient)" height="66" width="232" y="-33" x="-116" data-id="I" style=""
class="basic label-container"/>
<g transform="translate(-100, -21)" style="" class="label">
<rect/>
<foreignObject height="42" width="200">
<div style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>业务逻辑: internal/logic\n用户管理、代理配置、流量统计</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(213.1953125, 854)" data-look="neo" data-et="node" data-node="true" data-id="J"
id="flowchart-J-13" class="node default">
<rect stroke="url(#gradient)" height="45" width="188.390625" y="-22.5" x="-94.1953125" data-id="J"
style="" class="basic label-container"/>
<g transform="translate(-78.1953125, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="156.390625">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>代理核心: Xray / PPNode</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(458.5859375, 854)" data-look="neo" data-et="node" data-node="true" data-id="K"
id="flowchart-K-15" class="node default">
<rect stroke="url(#gradient)" height="45" width="202.390625" y="-22.5" x="-101.1953125" data-id="K"
style="" class="basic label-container"/>
<g transform="translate(-85.1953125, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="170.390625">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>队列服务: queue\n异步任务</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(725.78125, 854)" data-look="neo" data-et="node" data-node="true" data-id="L"
id="flowchart-L-17" class="node default">
<rect stroke="url(#gradient)" height="66" width="232" y="-33" x="-116" data-id="L" style=""
class="basic label-container"/>
<g transform="translate(-100, -21)" style="" class="label">
<rect/>
<foreignObject height="42" width="200">
<div style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>定时任务: scheduler\n数据清理、状态同步</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1024.9765625, 854)" data-look="neo" data-et="node" data-node="true" data-id="M"
id="flowchart-M-19" class="node default">
<rect stroke="url(#gradient)" height="45" width="183.71875" y="-22.5" x="-91.859375" data-id="M"
style="" class="basic label-container"/>
<g transform="translate(-75.859375, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="151.71875">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>数据模型: internal/model</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(124, 203)" data-look="neo" data-et="node" data-node="true" data-id="N"
id="flowchart-N-20" class="node default">
<rect stroke="url(#gradient)" height="66" width="232" y="-33" x="-116" data-id="N" style=""
class="basic label-container"/>
<g transform="translate(-100, -21)" style="" class="label">
<rect/>
<foreignObject height="42" width="200">
<div style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>初始化: initialize\n配置加载、DB连接</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(124, 41)" data-look="neo" data-et="node" data-node="true" data-id="O"
id="flowchart-O-22" class="node default">
<rect stroke="url(#gradient)" height="45" width="193.84375" y="-22.5" x="-96.921875" data-id="O"
style="" class="basic label-container"/>
<g transform="translate(-80.921875, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="161.84375">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>配置文件: etc/ppanel.yaml</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1397.3046875, 601)" data-look="neo" data-et="node" data-node="true" data-id="P"
id="flowchart-P-24" class="node default">
<rect stroke="url(#gradient)" height="66" width="232" y="-33" x="-116" data-id="P" style=""
class="basic label-container"/>
<g transform="translate(-100, -21)" style="" class="label">
<rect/>
<foreignObject height="42" width="200">
<div style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>公共工具: pkg\n日志、加密、HTTP客户端</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1024.9765625, 1005.5)" data-look="neo" data-et="node" data-node="true"
data-id="Q" id="flowchart-Q-27" class="node default">
<rect stroke="url(#gradient)" height="45" width="128.46875" y="-22.5" x="-64.234375" data-id="Q"
style="" class="basic label-container"/>
<g transform="translate(-48.234375, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="96.46875">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>数据库: MySQL</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1036.5234375, 1121.5)" data-look="neo" data-et="node" data-node="true"
data-id="R" id="flowchart-R-29" class="node default">
<rect stroke="url(#gradient)" height="45" width="103.578125" y="-22.5" x="-51.7890625" data-id="R"
style="" class="basic label-container"/>
<g transform="translate(-35.7890625, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="71.578125">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>缓存: Redis</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(1377.421875, 854)" data-look="neo" data-et="node" data-node="true" data-id="S"
id="flowchart-S-31" class="node default">
<rect stroke="url(#gradient)" height="45" width="192.234375" y="-22.5" x="-96.1171875" data-id="S"
style="" class="basic label-container"/>
<g transform="translate(-80.1171875, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="160.234375">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>外部服务: 支付 SDK, 通知</p>
</span>
</div>
</foreignObject>
</g>
</g>
<g transform="translate(474.69140625, 203)" data-look="neo" data-et="node" data-node="true" data-id="T"
id="flowchart-T-32" class="node default">
<rect stroke="url(#gradient)" height="45" width="167.375" y="-22.5" x="-83.6875" data-id="T"
style="" class="basic label-container"/>
<g transform="translate(-67.6875, -10.5)" style="" class="label">
<rect/>
<foreignObject height="21" width="135.375">
<div style="display: table-cell; white-space: normal; line-height: 1.5; max-width: 200px; text-align: center;"
xmlns="http://www.w3.org/1999/xhtml">
<span class="nodeLabel">
<p>Docker / Linux 服务器</p>
</span>
</div>
</foreignObject>
</g>
</g>
</g>
</g>
</g>
<defs>
<filter width="130%" height="130%" id="drop-shadow">
<feDropShadow flood-color="#FFFFFF" flood-opacity="0.06" stdDeviation="0" dy="4" dx="4"/>
</filter>
</defs>
<defs>
<filter width="150%" height="150%" id="drop-shadow-small">
<feDropShadow flood-color="#FFFFFF" flood-opacity="0.06" stdDeviation="0" dy="2" dx="2"/>
</filter>
</defs>
<linearGradient y2="0%" x2="100%" y1="0%" x1="0%" gradientUnits="objectBoundingBox" id="my-svg-gradient">
<stop stop-opacity="1" stop-color="#0042eb" offset="0%"/>
<stop stop-opacity="1" stop-color="#eb0042" offset="100%"/>
</linearGradient>
</svg>

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -3,7 +3,7 @@
<div align="center">
[![License](https://img.shields.io/github/license/perfect-panel/server)](LICENSE)
![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue)
[![Go Version](https://img.shields.io/badge/Go-1.21%2B-blue)](https://go.dev/)
[![Go Report Card](https://goreportcard.com/badge/github.com/perfect-panel/server)](https://goreportcard.com/report/github.com/perfect-panel/server)
[![Docker](https://img.shields.io/badge/Docker-Available-blue)](Dockerfile)
[![CI/CD](https://img.shields.io/github/actions/workflow/status/perfect-panel/server/release.yml)](.github/workflows/release.yml)
@ -124,6 +124,10 @@ PPanel 服务端是 PPanel 项目的后端组件,为代理服务提供强大
访问 [ppanel.dev](https://ppanel.dev) 获取更多信息。
## 🏛 系统架构
![Architecture Diagram](./doc/image/architecture.svg)
## 📁 目录结构
```