diff --git a/README.md b/README.md index 8f9e308..d1ef54e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ - [ ] Limiter - [ ] Compress +有些地方在用手动事务,有时间改成自动事务 + +remote 用环境变量保存账号密码! + +重新手动实现 model 层 + 环境变量配置默认会话配置 oauth token 验证授权范围 @@ -50,3 +56,8 @@ captcha_id 关联用户本机信息,实现验证码设备绑定(或者其他 | proxy/shared-static | pss | 动态代理 | | proxy/shared-rotate | psr | 隧道代理 | | proxy/private-static | pps | 独享代理 | + +### 外部服务 + +服务器ip 110.40.82.248 +api账密:api:123456 \ No newline at end of file diff --git a/cmd/main/main.go b/cmd/main/main.go index b3687fd..8988691 100644 --- a/cmd/main/main.go +++ b/cmd/main/main.go @@ -4,10 +4,10 @@ import ( "log/slog" "os" "os/signal" - "platform/init/env" - "platform/init/logs" - "platform/init/orm" - "platform/init/rds" + "platform/pkg/env" + "platform/pkg/logs" + "platform/pkg/orm" + "platform/pkg/rds" "platform/web" "syscall" ) diff --git a/cmd/playground/main.go b/cmd/playground/main.go new file mode 100644 index 0000000..4c637bc --- /dev/null +++ b/cmd/playground/main.go @@ -0,0 +1,7 @@ +package main + +import "encoding/base64" + +func main() { + println(base64.URLEncoding.EncodeToString([]byte("app:123456"))) +} diff --git a/cmd/tasks/main.go b/cmd/tasks/main.go new file mode 100644 index 0000000..beba0e2 --- /dev/null +++ b/cmd/tasks/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "context" + "errors" + "log/slog" + "platform/pkg/env" + "platform/pkg/logs" + "platform/pkg/orm" + "platform/pkg/rds" + "time" + + "github.com/redis/go-redis/v9" +) + +func main() { + Start() +} + +func Start() { + ctx := context.Background() + + env.Init() + logs.Init() + rds.Init() + orm.Init() + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + for curr := range ticker.C { + err := process(ctx, curr) + if err != nil { + panic(err) + } + } +} + +func process(ctx context.Context, curr time.Time) error { + + // 获取并删除 + script := redis.NewScript(` + local result = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1]) + if #result > 0 then + redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, ARGV[1]) + end + return result + `) + + // 计算时间范围 + // 执行脚本 + result, err := script.Run(ctx, rds.Client, []string{"tasks:session"}, curr.Unix()).Result() + if err != nil { + return err + } + + // 处理结果 + list, ok := result.([]string) + if !ok { + return errors.New("failed to convert result to []string") + } + for _, item := range list { + // 从数据库删除授权信息 + slog.Debug(item) + } + + return nil +} diff --git a/cmd/wrapper/main.go b/cmd/wrapper/main.go new file mode 100644 index 0000000..490bf07 --- /dev/null +++ b/cmd/wrapper/main.go @@ -0,0 +1,85 @@ +package main + +import ( + "context" + "log/slog" + "os" + "os/signal" + "platform/pkg/env" + "platform/pkg/logs" + "time" +) + +func main() { + + // 初始化环境 + env.Init() + logs.Init() + + // 上下文 + ctx, cancel := context.WithCancel(context.Background()) + + // 监听退出 + exit := make(chan os.Signal, 1) + defer close(exit) + signal.Notify(exit, os.Interrupt, os.Kill) + defer signal.Stop(exit) + + // 启动管理子线程 + errCh := make(chan error, 1) + go func() { + defer close(errCh) + err := start(ctx) + if err != nil { + errCh <- err + } + }() + + select { + case <-exit: + slog.Debug("exit by signal") + cancel() + case err := <-errCh: + slog.Error("exit by error", "error", err) + } +} + +// 连接池,硬编码提供 10000 的容量 +var idle = 100 +var maximum = 10000 + +var pool = make(map[string]*Node, 10000) + +var tick = 1 * time.Minute + +type Node struct { + Ip string +} + +var last time.Time + +func start(ctx context.Context) error { + ticker := time.NewTicker(tick) + go func() { + <-ctx.Done() + ticker.Stop() + }() + + for curr := range ticker.C { + last = curr + go func() { + process(ctx, curr) + }() + } + + return nil +} + +func process(ctx context.Context, curr time.Time) { + + // 查询节点状态 + + // 筛选在线节点添加到节点池 + + // +} diff --git a/docs/proxy 并发协程数分析.excalidraw b/docs/proxy 并发协程数分析.excalidraw new file mode 100644 index 0000000..abf8ea8 --- /dev/null +++ b/docs/proxy 并发协程数分析.excalidraw @@ -0,0 +1,2246 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "PUMIiEchmuGqQyIOCFrRb", + "type": "rectangle", + "x": 515, + "y": 573, + "width": 122, + "height": 109, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { + "type": 3 + }, + "seed": 834957798, + "version": 248, + "versionNonce": 771308454, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "84yJpP5f4gWHkvTbfwW5P" + }, + { + "id": "iYH8RdCRTtlbcoHof5J0Z", + "type": "arrow" + }, + { + "id": "aTZISiCXS8EBqmqXXfJNN", + "type": "arrow" + } + ], + "updated": 1740459342129, + "link": null, + "locked": false + }, + { + "id": "84yJpP5f4gWHkvTbfwW5P", + "type": "text", + "x": 546.3400268554688, + "y": 615, + "width": 59.3199462890625, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a1", + "roundness": null, + "seed": 695398394, + "version": 212, + "versionNonce": 1071568614, + "isDeleted": false, + "boundElements": [], + "updated": 1740459342129, + "link": null, + "locked": false, + "text": "server", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PUMIiEchmuGqQyIOCFrRb", + "originalText": "server", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "JZeeDLEWbT6GPA8U6eb8e", + "type": "rectangle", + "x": 776, + "y": 456, + "width": 121, + "height": 117, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a2", + "roundness": { + "type": 3 + }, + "seed": 1171398522, + "version": 248, + "versionNonce": 1563510661, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "OWnTgOvnUDofRrZn42zU3" + }, + { + "id": "iYH8RdCRTtlbcoHof5J0Z", + "type": "arrow" + }, + { + "id": "pBo93FpE0g1HhaXmKzDcb", + "type": "arrow" + }, + { + "id": "OsDtRJz_Eot2QullVEkgL", + "type": "arrow" + }, + { + "id": "aJMPDGuINcdk7BuCIyWkh", + "type": "arrow" + }, + { + "id": "OtljG6qpY_9sm0cW3fbCS", + "type": "arrow" + } + ], + "updated": 1740465100523, + "link": null, + "locked": false + }, + { + "id": "OWnTgOvnUDofRrZn42zU3", + "type": "text", + "x": 799.7500305175781, + "y": 502, + "width": 73.49993896484375, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a3", + "roundness": null, + "seed": 578886694, + "version": 192, + "versionNonce": 205599718, + "isDeleted": false, + "boundElements": [], + "updated": 1740459342129, + "link": null, + "locked": false, + "text": "fwd srv", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "JZeeDLEWbT6GPA8U6eb8e", + "originalText": "fwd srv", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "qeh4s95Bj6-0vpeYuUONq", + "type": "rectangle", + "x": 777, + "y": 690, + "width": 122.99999999999997, + "height": 111, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a4", + "roundness": { + "type": 3 + }, + "seed": 1778868966, + "version": 214, + "versionNonce": 705499366, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Jclwbg8WyG8-DXY36M18G" + }, + { + "id": "aTZISiCXS8EBqmqXXfJNN", + "type": "arrow" + } + ], + "updated": 1740459342130, + "link": null, + "locked": false + }, + { + "id": "Jclwbg8WyG8-DXY36M18G", + "type": "text", + "x": 801.8500289916992, + "y": 733, + "width": 73.29994201660156, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a5", + "roundness": null, + "seed": 436440250, + "version": 186, + "versionNonce": 1296434214, + "isDeleted": false, + "boundElements": [], + "updated": 1740459342130, + "link": null, + "locked": false, + "text": "web srv", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "qeh4s95Bj6-0vpeYuUONq", + "originalText": "web srv", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "iYH8RdCRTtlbcoHof5J0Z", + "type": "arrow", + "x": 648, + "y": 593.913329595227, + "width": 121, + "height": 77.68557665416654, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a6", + "roundness": { + "type": 2 + }, + "seed": 1256063482, + "version": 448, + "versionNonce": 385561850, + "isDeleted": false, + "boundElements": [], + "updated": 1740459342205, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 121, + -77.68557665416654 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "PUMIiEchmuGqQyIOCFrRb", + "focus": 0.1349449566751027, + "gap": 11, + "fixedPoint": null + }, + "endBinding": { + "elementId": "JZeeDLEWbT6GPA8U6eb8e", + "focus": 0.4274506927014281, + "gap": 7, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "aTZISiCXS8EBqmqXXfJNN", + "type": "arrow", + "x": 647, + "y": 663.7833967214427, + "width": 121, + "height": 81.20243265698639, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a7", + "roundness": { + "type": 2 + }, + "seed": 426354342, + "version": 450, + "versionNonce": 450477690, + "isDeleted": false, + "boundElements": [], + "updated": 1740459342206, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 121, + 81.20243265698639 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "PUMIiEchmuGqQyIOCFrRb", + "focus": -0.11907687637248585, + "gap": 10, + "fixedPoint": null + }, + "endBinding": { + "elementId": "qeh4s95Bj6-0vpeYuUONq", + "focus": -0.48358864391397727, + "gap": 9, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "ARK-rHchkdloRH_U6R-cf", + "type": "rectangle", + "x": 979, + "y": 325, + "width": 117, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a8", + "roundness": { + "type": 3 + }, + "seed": 2122154214, + "version": 303, + "versionNonce": 601733158, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "9Mo2bbnfs8kWT3k5nhcuQ" + }, + { + "id": "viIAn9gzZu9oSTj-eoyhu", + "type": "arrow" + }, + { + "id": "pBo93FpE0g1HhaXmKzDcb", + "type": "arrow" + } + ], + "updated": 1740460802933, + "link": null, + "locked": false + }, + { + "id": "9Mo2bbnfs8kWT3k5nhcuQ", + "type": "text", + "x": 1003.5100250244141, + "y": 337.5, + "width": 67.97994995117188, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a9", + "roundness": null, + "seed": 869224934, + "version": 265, + "versionNonce": 1828701030, + "isDeleted": false, + "boundElements": [], + "updated": 1740460802933, + "link": null, + "locked": false, + "text": "ctrl les", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ARK-rHchkdloRH_U6R-cf", + "originalText": "ctrl les", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "o3DmsYnyGUKptyC_bI1-_", + "type": "rectangle", + "x": 985, + "y": 637, + "width": 117, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aA", + "roundness": { + "type": 3 + }, + "seed": 1238575610, + "version": 456, + "versionNonce": 1365713318, + "isDeleted": false, + "boundElements": [ + { + "id": "qcYZzHMbq1x1lrS_yDj0b", + "type": "text" + }, + { + "id": "OsDtRJz_Eot2QullVEkgL", + "type": "arrow" + }, + { + "id": "FORHUUbuC-4F1i8uQm3KN", + "type": "arrow" + } + ], + "updated": 1740459355379, + "link": null, + "locked": false + }, + { + "id": "qcYZzHMbq1x1lrS_yDj0b", + "type": "text", + "x": 1003.3500289916992, + "y": 649.5, + "width": 80.29994201660156, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aB", + "roundness": null, + "seed": 1338509990, + "version": 424, + "versionNonce": 1218444518, + "isDeleted": false, + "boundElements": [], + "updated": 1740459355379, + "link": null, + "locked": false, + "text": "data les", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "o3DmsYnyGUKptyC_bI1-_", + "originalText": "data les", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "pBo93FpE0g1HhaXmKzDcb", + "type": "arrow", + "x": 908, + "y": 467.687980255408, + "width": 61, + "height": 89.5916573770869, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aC", + "roundness": { + "type": 2 + }, + "seed": 874520614, + "version": 563, + "versionNonce": 977707494, + "isDeleted": false, + "boundElements": [], + "updated": 1740460802933, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 61, + -89.5916573770869 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "JZeeDLEWbT6GPA8U6eb8e", + "focus": 0.21257569553463726, + "gap": 11, + "fixedPoint": null + }, + "endBinding": { + "elementId": "ARK-rHchkdloRH_U6R-cf", + "focus": 0.6537214966513702, + "gap": 10, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "OsDtRJz_Eot2QullVEkgL", + "type": "arrow", + "x": 907, + "y": 565.0497586857862, + "width": 69, + "height": 79.50000983425457, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aD", + "roundness": { + "type": 2 + }, + "seed": 1461075706, + "version": 827, + "versionNonce": 1922789414, + "isDeleted": false, + "boundElements": [], + "updated": 1740459355379, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 69, + 79.50000983425457 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "JZeeDLEWbT6GPA8U6eb8e", + "focus": -0.19714195746796065, + "gap": 10, + "fixedPoint": null + }, + "endBinding": { + "elementId": "o3DmsYnyGUKptyC_bI1-_", + "focus": -0.6528148556841264, + "gap": 9, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "nQcyYWmYQGKR8s25XeHOW", + "type": "diamond", + "x": 1193, + "y": 448, + "width": 198.9999999999999, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ab", + "roundness": { + "type": 2 + }, + "seed": 822142138, + "version": 914, + "versionNonce": 799475450, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "7JTnVMS_ONvhBxUkdR7oU" + }, + { + "id": "DXl9-V4hPvM4fVACUaQ2u", + "type": "arrow" + }, + { + "id": "RNJX06B9BggyeEmvrjTzJ", + "type": "arrow" + } + ], + "updated": 1740459367027, + "link": null, + "locked": false + }, + { + "id": "7JTnVMS_ONvhBxUkdR7oU", + "type": "text", + "x": 1264.140022277832, + "y": 485.5, + "width": 56.21995544433594, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ac", + "roundness": null, + "seed": 448389926, + "version": 927, + "versionNonce": 830966182, + "isDeleted": false, + "boundElements": [], + "updated": 1740459363562, + "link": null, + "locked": false, + "text": "nodes", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "nQcyYWmYQGKR8s25XeHOW", + "originalText": "nodes", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "yZVNUVj8AODBWNPibg7OB", + "type": "rectangle", + "x": 1211, + "y": 320, + "width": 159, + "height": 51, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ah", + "roundness": { + "type": 3 + }, + "seed": 1893408166, + "version": 341, + "versionNonce": 1018379130, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "6498xpfOxW7-3-gRdwal9" + }, + { + "id": "viIAn9gzZu9oSTj-eoyhu", + "type": "arrow" + }, + { + "id": "jnpcb69No7JDBc9aE7kZB", + "type": "arrow" + }, + { + "id": "DXl9-V4hPvM4fVACUaQ2u", + "type": "arrow" + } + ], + "updated": 1740459360265, + "link": null, + "locked": false + }, + { + "id": "6498xpfOxW7-3-gRdwal9", + "type": "text", + "x": 1239.7800369262695, + "y": 333, + "width": 101.43992614746094, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ai", + "roundness": null, + "seed": 1804103782, + "version": 271, + "versionNonce": 141671482, + "isDeleted": false, + "boundElements": [], + "updated": 1740459360265, + "link": null, + "locked": false, + "text": "...ctrl conn", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "yZVNUVj8AODBWNPibg7OB", + "originalText": "...ctrl conn", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "viIAn9gzZu9oSTj-eoyhu", + "type": "arrow", + "x": 1106, + "y": 350.4943123597815, + "width": 96, + "height": 0.9915054263509546, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aj", + "roundness": { + "type": 2 + }, + "seed": 1389238714, + "version": 549, + "versionNonce": 876504742, + "isDeleted": false, + "boundElements": [], + "updated": 1740460802933, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 96, + -0.9915054263509546 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "ARK-rHchkdloRH_U6R-cf", + "focus": 0.04693733289932182, + "gap": 10, + "fixedPoint": null + }, + "endBinding": { + "elementId": "yZVNUVj8AODBWNPibg7OB", + "focus": -0.09656845140902404, + "gap": 9, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "wNIaUBulqf3IrfrIEOsE7", + "type": "rectangle", + "x": 1595, + "y": 321, + "width": 168, + "height": 53, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "al", + "roundness": { + "type": 3 + }, + "seed": 1507006822, + "version": 326, + "versionNonce": 550468346, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "z-Ms6uL7qxaeBipp1TeRT" + }, + { + "id": "jnpcb69No7JDBc9aE7kZB", + "type": "arrow" + }, + { + "id": "TAFt8ZsSngPp6Xsh7w4GV", + "type": "arrow" + } + ], + "updated": 1740459398009, + "link": null, + "locked": false + }, + { + "id": "z-Ms6uL7qxaeBipp1TeRT", + "type": "text", + "x": 1635.7800369262695, + "y": 335, + "width": 86.43992614746094, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "am", + "roundness": null, + "seed": 468123962, + "version": 285, + "versionNonce": 769264570, + "isDeleted": false, + "boundElements": [], + "updated": 1740459398009, + "link": null, + "locked": false, + "text": "...fwd les", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "wNIaUBulqf3IrfrIEOsE7", + "originalText": "...fwd les", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "jnpcb69No7JDBc9aE7kZB", + "type": "arrow", + "x": 1380.4956162600222, + "y": 349.0571526579183, + "width": 204.5043837399778, + "height": 2.7664265619687853, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "an", + "roundness": { + "type": 2 + }, + "seed": 2101724858, + "version": 683, + "versionNonce": 81036410, + "isDeleted": false, + "boundElements": [], + "updated": 1740459398009, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 204.5043837399778, + -2.7664265619687853 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "yZVNUVj8AODBWNPibg7OB", + "focus": 0.18294068183201054, + "gap": 10.495616260022189, + "fixedPoint": null + }, + "endBinding": { + "elementId": "wNIaUBulqf3IrfrIEOsE7", + "focus": 0.08976799582243951, + "gap": 10, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "xXmfcqOvkPRQKGDg0XAAT", + "type": "diamond", + "x": 1604, + "y": 753, + "width": 178, + "height": 87, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ao", + "roundness": { + "type": 2 + }, + "seed": 1897896614, + "version": 588, + "versionNonce": 121974266, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "9r-IrPmazdcWBUsvmY1Vu" + }, + { + "id": "MILFx4u1KlHcwV_PVDqqm", + "type": "arrow" + } + ], + "updated": 1740459398009, + "link": null, + "locked": false + }, + { + "id": "9r-IrPmazdcWBUsvmY1Vu", + "type": "text", + "x": 1667.1700286865234, + "y": 784.25, + "width": 51.659942626953125, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ap", + "roundness": null, + "seed": 150831782, + "version": 525, + "versionNonce": 1092705978, + "isDeleted": false, + "boundElements": [], + "updated": 1740459398009, + "link": null, + "locked": false, + "text": "users", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "xXmfcqOvkPRQKGDg0XAAT", + "originalText": "users", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "NX1GnOlMfYwfnm3IGWZ0-", + "type": "rectangle", + "x": 1603, + "y": 626.5, + "width": 168, + "height": 53, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "as", + "roundness": { + "type": 3 + }, + "seed": 976992614, + "version": 386, + "versionNonce": 1068398842, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "O_qWPhU1bQHHhbz13MVKL" + }, + { + "id": "TAFt8ZsSngPp6Xsh7w4GV", + "type": "arrow" + }, + { + "id": "yltEBimMd8JosK4fS_rd6", + "type": "arrow" + }, + { + "id": "MILFx4u1KlHcwV_PVDqqm", + "type": "arrow" + }, + { + "id": "-Pr6qQo_544oRjAP51JxA", + "type": "arrow" + }, + { + "id": "MgkuioUvQn369ODe58Bhf", + "type": "arrow" + } + ], + "updated": 1740459469311, + "link": null, + "locked": false + }, + { + "id": "O_qWPhU1bQHHhbz13MVKL", + "type": "text", + "x": 1635.2700424194336, + "y": 640.5, + "width": 103.45991516113281, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "at", + "roundness": null, + "seed": 579574950, + "version": 337, + "versionNonce": 667508986, + "isDeleted": false, + "boundElements": [], + "updated": 1740459398009, + "link": null, + "locked": false, + "text": "...fwd conn", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "NX1GnOlMfYwfnm3IGWZ0-", + "originalText": "...fwd conn", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "TAFt8ZsSngPp6Xsh7w4GV", + "type": "arrow", + "x": 1676.7197414622267, + "y": 382, + "width": 10.199037889868805, + "height": 233, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "au", + "roundness": { + "type": 2 + }, + "seed": 535910310, + "version": 687, + "versionNonce": 1774877286, + "isDeleted": false, + "boundElements": [], + "updated": 1740459398490, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 10.199037889868805, + 233 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "wNIaUBulqf3IrfrIEOsE7", + "focus": 0.04450936541171166, + "gap": 8, + "fixedPoint": null + }, + "endBinding": { + "elementId": "NX1GnOlMfYwfnm3IGWZ0-", + "focus": 0.018578466333455737, + "gap": 11.5, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "hXsAXDmjIi-7T-kcJXJh8", + "type": "rectangle", + "x": 1226, + "y": 628, + "width": 152.00000000000003, + "height": 58, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b02", + "roundness": { + "type": 3 + }, + "seed": 1286606246, + "version": 142, + "versionNonce": 1431554470, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "4volfi6LubAut_MQe0-56" + }, + { + "id": "yltEBimMd8JosK4fS_rd6", + "type": "arrow" + }, + { + "id": "FORHUUbuC-4F1i8uQm3KN", + "type": "arrow" + }, + { + "id": "RNJX06B9BggyeEmvrjTzJ", + "type": "arrow" + }, + { + "id": "W8MAmqEOoH-FBlwjAOgML", + "type": "arrow" + }, + { + "id": "KDRGpdsh-p9pYg4VxMXdE", + "type": "arrow" + } + ], + "updated": 1740459464807, + "link": null, + "locked": false + }, + { + "id": "4volfi6LubAut_MQe0-56", + "type": "text", + "x": 1250.6500396728516, + "y": 644.5, + "width": 102.69992065429688, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b03", + "roundness": null, + "seed": 1185909094, + "version": 90, + "versionNonce": 2138739686, + "isDeleted": false, + "boundElements": [], + "updated": 1740459355379, + "link": null, + "locked": false, + "text": "...daa conn", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hXsAXDmjIi-7T-kcJXJh8", + "originalText": "...daa conn", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "yltEBimMd8JosK4fS_rd6", + "type": "arrow", + "x": 1593, + "y": 654.889597222672, + "width": 207, + "height": 3.09593973117137, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b04", + "roundness": { + "type": 2 + }, + "seed": 996104102, + "version": 219, + "versionNonce": 1460464250, + "isDeleted": false, + "boundElements": [], + "updated": 1740459398009, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -207, + 3.09593973117137 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "NX1GnOlMfYwfnm3IGWZ0-", + "focus": -0.017427075668983442, + "gap": 10, + "fixedPoint": null + }, + "endBinding": { + "elementId": "hXsAXDmjIi-7T-kcJXJh8", + "focus": 0.0788090308201251, + "gap": 8, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "FORHUUbuC-4F1i8uQm3KN", + "type": "arrow", + "x": 1110, + "y": 659.5112209187108, + "width": 109, + "height": 1.7675088328977608, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b05", + "roundness": { + "type": 2 + }, + "seed": 1862188282, + "version": 150, + "versionNonce": 1069697978, + "isDeleted": false, + "boundElements": [], + "updated": 1740459355840, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 109, + 1.7675088328977608 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "o3DmsYnyGUKptyC_bI1-_", + "focus": -0.1374686600376961, + "gap": 8, + "fixedPoint": null + }, + "endBinding": { + "elementId": "hXsAXDmjIi-7T-kcJXJh8", + "focus": -0.18604651162790478, + "gap": 7, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "DXl9-V4hPvM4fVACUaQ2u", + "type": "arrow", + "x": 1292.3133053774181, + "y": 443.01472889175926, + "width": 3.706741992028128, + "height": 61.01472889175926, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b06", + "roundness": { + "type": 2 + }, + "seed": 1089204602, + "version": 503, + "versionNonce": 410215654, + "isDeleted": false, + "boundElements": [], + "updated": 1740459363562, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -3.706741992028128, + -61.01472889175926 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "nQcyYWmYQGKR8s25XeHOW", + "focus": 0.03169596183568608, + "gap": 4.988765669457798, + "fixedPoint": null + }, + "endBinding": { + "elementId": "yZVNUVj8AODBWNPibg7OB", + "focus": 0.05676100724401311, + "gap": 11, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "MILFx4u1KlHcwV_PVDqqm", + "type": "arrow", + "x": 1690.6643244393063, + "y": 745.7399130928961, + "width": 3.4864413424775194, + "height": 55.73991309289613, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b07", + "roundness": { + "type": 2 + }, + "seed": 1415220666, + "version": 241, + "versionNonce": 789551334, + "isDeleted": false, + "boundElements": [], + "updated": 1740459398490, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -3.4864413424775194, + -55.73991309289613 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "xXmfcqOvkPRQKGDg0XAAT", + "focus": 0.009430176565010663, + "gap": 7.548310741227745, + "fixedPoint": null + }, + "endBinding": { + "elementId": "NX1GnOlMfYwfnm3IGWZ0-", + "focus": 0.024941262745458458, + "gap": 10.5, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "Y84X9IRRjW5zcf3d0pgmR", + "type": "rectangle", + "x": 1437, + "y": 553, + "width": 112.99999999999997, + "height": 55.99999999999999, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b08", + "roundness": { + "type": 3 + }, + "seed": 878312890, + "version": 234, + "versionNonce": 1736740730, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "O85B3auEIwDx719hWrozB" + }, + { + "id": "W8MAmqEOoH-FBlwjAOgML", + "type": "arrow" + }, + { + "id": "MgkuioUvQn369ODe58Bhf", + "type": "arrow" + } + ], + "updated": 1740459469311, + "link": null, + "locked": false + }, + { + "id": "O85B3auEIwDx719hWrozB", + "type": "text", + "x": 1451.8700332641602, + "y": 568.5, + "width": 83.25993347167969, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b08V", + "roundness": null, + "seed": 1045619002, + "version": 200, + "versionNonce": 1129439654, + "isDeleted": false, + "boundElements": [], + "updated": 1740459457390, + "link": null, + "locked": false, + "text": "...copy in", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Y84X9IRRjW5zcf3d0pgmR", + "originalText": "...copy in", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "T-QlEHtAGQPUBYNjTJp3S", + "type": "rectangle", + "x": 1436, + "y": 694, + "width": 116.99999999999999, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b09", + "roundness": { + "type": 3 + }, + "seed": 613735482, + "version": 176, + "versionNonce": 1029576934, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "SskX-RER9Ju0cVIssekiU" + }, + { + "id": "KDRGpdsh-p9pYg4VxMXdE", + "type": "arrow" + }, + { + "id": "-Pr6qQo_544oRjAP51JxA", + "type": "arrow" + } + ], + "updated": 1740459466663, + "link": null, + "locked": false + }, + { + "id": "SskX-RER9Ju0cVIssekiU", + "type": "text", + "x": 1443.5600280761719, + "y": 711.5, + "width": 101.87994384765625, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0A", + "roundness": null, + "seed": 440368806, + "version": 155, + "versionNonce": 685153722, + "isDeleted": false, + "boundElements": [], + "updated": 1740459444226, + "link": null, + "locked": false, + "text": "...copy out", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "T-QlEHtAGQPUBYNjTJp3S", + "originalText": "...copy out", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "RNJX06B9BggyeEmvrjTzJ", + "type": "arrow", + "x": 1294, + "y": 558, + "width": 3, + "height": 59, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0F", + "roundness": { + "type": 2 + }, + "seed": 996139878, + "version": 35, + "versionNonce": 566897210, + "isDeleted": false, + "boundElements": [], + "updated": 1740459372367, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 3, + 59 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "nQcyYWmYQGKR8s25XeHOW", + "focus": 0.015586406609317783, + "gap": 9.608786796150554, + "fixedPoint": null + }, + "endBinding": { + "elementId": "hXsAXDmjIi-7T-kcJXJh8", + "focus": -0.038284839203675335, + "gap": 11, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "W8MAmqEOoH-FBlwjAOgML", + "type": "arrow", + "x": 1388, + "y": 622, + "width": 39, + "height": 27, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0G", + "roundness": { + "type": 2 + }, + "seed": 1258343738, + "version": 21, + "versionNonce": 253699046, + "isDeleted": false, + "boundElements": [], + "updated": 1740459476102, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 39, + -27 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "hXsAXDmjIi-7T-kcJXJh8", + "focus": 0.30065975494816205, + "gap": 10, + "fixedPoint": null + }, + "endBinding": { + "elementId": "Y84X9IRRjW5zcf3d0pgmR", + "focus": 0.4773638968481376, + "gap": 10, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "KDRGpdsh-p9pYg4VxMXdE", + "type": "arrow", + "x": 1382, + "y": 688, + "width": 47, + "height": 29, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0H", + "roundness": { + "type": 2 + }, + "seed": 1300682150, + "version": 33, + "versionNonce": 1354888570, + "isDeleted": false, + "boundElements": [], + "updated": 1740459476102, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 47, + 29 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "hXsAXDmjIi-7T-kcJXJh8", + "focus": -0.24194000560695264, + "gap": 4, + "fixedPoint": null + }, + "endBinding": { + "elementId": "T-QlEHtAGQPUBYNjTJp3S", + "focus": -0.5055528730082086, + "gap": 7, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "-Pr6qQo_544oRjAP51JxA", + "type": "arrow", + "x": 1566, + "y": 717, + "width": 37, + "height": 28, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0I", + "roundness": { + "type": 2 + }, + "seed": 96382458, + "version": 46, + "versionNonce": 1851823418, + "isDeleted": false, + "boundElements": [], + "updated": 1740459482142, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 37, + -28 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "T-QlEHtAGQPUBYNjTJp3S", + "focus": 0.6342794759825329, + "gap": 13, + "fixedPoint": null + }, + "endBinding": { + "elementId": "NX1GnOlMfYwfnm3IGWZ0-", + "focus": 0.30607651912978245, + "gap": 9.5, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "MgkuioUvQn369ODe58Bhf", + "type": "arrow", + "x": 1561.1762226439046, + "y": 594.3568940101158, + "width": 41.64755471219041, + "height": 20.286211979768723, + "angle": 0.0808496331090609, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0J", + "roundness": { + "type": 2 + }, + "seed": 1752771258, + "version": 73, + "versionNonce": 1989510950, + "isDeleted": false, + "boundElements": [], + "updated": 1740459490694, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 41.64755471219041, + 20.286211979768723 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Y84X9IRRjW5zcf3d0pgmR", + "focus": -0.4699122445656447, + "gap": 12.060772448905709, + "fixedPoint": null + }, + "endBinding": { + "elementId": "NX1GnOlMfYwfnm3IGWZ0-", + "focus": -0.17834784400064657, + "gap": 10.226316460129738, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "XXPsjpjVVITnc6rieErSj", + "type": "rectangle", + "x": 842, + "y": 365, + "width": 84.99999999999999, + "height": 38, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0K", + "roundness": { + "type": 3 + }, + "seed": 73696613, + "version": 128, + "versionNonce": 661767205, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "3Dtj0e-IBgyqdwHiW5bQS" + }, + { + "id": "aJMPDGuINcdk7BuCIyWkh", + "type": "arrow" + } + ], + "updated": 1740465113266, + "link": null, + "locked": false + }, + { + "id": "3Dtj0e-IBgyqdwHiW5bQS", + "type": "text", + "x": 852.3900299072266, + "y": 371.5, + "width": 64.21994018554688, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0KV", + "roundness": null, + "seed": 1771404901, + "version": 63, + "versionNonce": 1867511685, + "isDeleted": false, + "boundElements": null, + "updated": 1740465113266, + "link": null, + "locked": false, + "text": "accept", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "XXPsjpjVVITnc6rieErSj", + "originalText": "accept", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "PqIofPuCzhjbqVXpjZBPi", + "type": "rectangle", + "x": 731, + "y": 365, + "width": 90.99999999999996, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0L", + "roundness": { + "type": 3 + }, + "seed": 896819531, + "version": 174, + "versionNonce": 650364043, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "K4t1GKHRQ3Z2LpqyE_nHZ" + }, + { + "id": "OtljG6qpY_9sm0cW3fbCS", + "type": "arrow" + } + ], + "updated": 1740465111299, + "link": null, + "locked": false + }, + { + "id": "K4t1GKHRQ3Z2LpqyE_nHZ", + "type": "text", + "x": 741.1200332641602, + "y": 372.5, + "width": 70.75993347167969, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0M", + "roundness": null, + "seed": 1569508523, + "version": 91, + "versionNonce": 177273643, + "isDeleted": false, + "boundElements": null, + "updated": 1740465111299, + "link": null, + "locked": false, + "text": "wg.wait", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PqIofPuCzhjbqVXpjZBPi", + "originalText": "wg.wait", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "aJMPDGuINcdk7BuCIyWkh", + "type": "arrow", + "x": 866, + "y": 448, + "width": 6.961475891473924, + "height": 37, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0N", + "roundness": { + "type": 2 + }, + "seed": 2062932139, + "version": 25, + "versionNonce": 936427237, + "isDeleted": false, + "boundElements": null, + "updated": 1740465113266, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 6.961475891473924, + -37 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "JZeeDLEWbT6GPA8U6eb8e", + "focus": 0.36327954644570426, + "gap": 8, + "fixedPoint": null + }, + "endBinding": { + "elementId": "XXPsjpjVVITnc6rieErSj", + "focus": 0.1401748138556167, + "gap": 8, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "OtljG6qpY_9sm0cW3fbCS", + "type": "arrow", + "x": 805, + "y": 448, + "width": 9.342333468498623, + "height": 35, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0O", + "roundness": { + "type": 2 + }, + "seed": 2046851237, + "version": 107, + "versionNonce": 164445771, + "isDeleted": false, + "boundElements": null, + "updated": 1740465115990, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -9.342333468498623, + -35 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "JZeeDLEWbT6GPA8U6eb8e", + "focus": -0.18064190537011374, + "gap": 8, + "fixedPoint": null + }, + "endBinding": { + "elementId": "PqIofPuCzhjbqVXpjZBPi", + "focus": -0.22982198945491664, + "gap": 8, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/数据表结构.excalidraw b/docs/数据表结构.excalidraw new file mode 100644 index 0000000..78ba35e --- /dev/null +++ b/docs/数据表结构.excalidraw @@ -0,0 +1,2472 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "sWVhyKNTaf6c4MTpx5iEK", + "type": "rectangle", + "x": 1200, + "y": 900, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b16", + "roundness": { + "type": 3 + }, + "seed": 1485411871, + "version": 186, + "versionNonce": 988130497, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "QC5avpKjA0ubD5Uww0Z04" + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow" + }, + { + "id": "YL-GQEx6_yX_IbTW-WC-i", + "type": "arrow" + }, + { + "id": "CZYYCHaD7DXcd4W6XNA--", + "type": "arrow" + }, + { + "id": "0mjZSajWpy3Yilllikfkw", + "type": "arrow" + }, + { + "id": "X14BlJ5OoZr9srJ2jqOq0", + "type": "arrow" + }, + { + "id": "k8yWnHxYqT8tFxjwpm1CE", + "type": "arrow" + }, + { + "id": "vI7N6o6RZuvuZqC72CrG0", + "type": "arrow" + } + ], + "updated": 1742547819139, + "link": null, + "locked": false + }, + { + "id": "QC5avpKjA0ubD5Uww0Z04", + "type": "text", + "x": 1278, + "y": 925, + "width": 44, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b17", + "roundness": null, + "seed": 2016096557, + "version": 129, + "versionNonce": 1606554369, + "isDeleted": false, + "boundElements": [], + "updated": 1742547653072, + "link": null, + "locked": false, + "text": "用户\nuser", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "sWVhyKNTaf6c4MTpx5iEK", + "originalText": "用户\nuser", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "6tM6EIcU9Lu7p7gOOmQwo", + "type": "rectangle", + "x": 1200, + "y": 1300, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b17G", + "roundness": { + "type": 3 + }, + "seed": 724897029, + "version": 209, + "versionNonce": 885626127, + "isDeleted": false, + "boundElements": [ + { + "id": "VO1P5bEva1XiIHszRSHAb", + "type": "text" + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow" + }, + { + "id": "YL-GQEx6_yX_IbTW-WC-i", + "type": "arrow" + }, + { + "id": "t9vgGJQkbyPaY_d19wdEf", + "type": "arrow" + }, + { + "id": "CZYYCHaD7DXcd4W6XNA--", + "type": "arrow" + }, + { + "id": "0mjZSajWpy3Yilllikfkw", + "type": "arrow" + }, + { + "id": "X14BlJ5OoZr9srJ2jqOq0", + "type": "arrow" + }, + { + "id": "vZgnKG8BBOWQG2tFtJ1LQ", + "type": "arrow" + }, + { + "id": "k8yWnHxYqT8tFxjwpm1CE", + "type": "arrow" + }, + { + "id": "ZGl1k8DptaKTTqI5hm8V3", + "type": "arrow" + } + ], + "updated": 1742547823273, + "link": null, + "locked": false + }, + { + "id": "VO1P5bEva1XiIHszRSHAb", + "type": "text", + "x": 1270, + "y": 1325, + "width": 60, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b17V", + "roundness": null, + "seed": 405926347, + "version": 172, + "versionNonce": 1442304929, + "isDeleted": false, + "boundElements": [], + "updated": 1742547783444, + "link": null, + "locked": false, + "text": "管理员\nadmin", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6tM6EIcU9Lu7p7gOOmQwo", + "originalText": "管理员\nadmin", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "h5j5btibJLrw6noInTqeP", + "type": "rectangle", + "x": 500, + "y": 900, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b17d", + "roundness": { + "type": 3 + }, + "seed": 683797792, + "version": 408, + "versionNonce": 1712936143, + "isDeleted": false, + "boundElements": [ + { + "id": "EST9AHZp3TyEeX246ziJX", + "type": "text" + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow" + }, + { + "id": "YL-GQEx6_yX_IbTW-WC-i", + "type": "arrow" + }, + { + "id": "t9vgGJQkbyPaY_d19wdEf", + "type": "arrow" + }, + { + "id": "CZYYCHaD7DXcd4W6XNA--", + "type": "arrow" + }, + { + "id": "0mjZSajWpy3Yilllikfkw", + "type": "arrow" + }, + { + "id": "X14BlJ5OoZr9srJ2jqOq0", + "type": "arrow" + }, + { + "id": "vZgnKG8BBOWQG2tFtJ1LQ", + "type": "arrow" + }, + { + "id": "k8yWnHxYqT8tFxjwpm1CE", + "type": "arrow" + }, + { + "id": "waO6Ar3IuQFLgZC7RgVw8", + "type": "arrow" + }, + { + "id": "42AGZYgRgHI8zEbq5zbGL", + "type": "arrow" + } + ], + "updated": 1742547850508, + "link": null, + "locked": false + }, + { + "id": "EST9AHZp3TyEeX246ziJX", + "type": "text", + "x": 567, + "y": 925, + "width": 66, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b17l", + "roundness": null, + "seed": 779547872, + "version": 395, + "versionNonce": 1288954607, + "isDeleted": false, + "boundElements": [], + "updated": 1742547850508, + "link": null, + "locked": false, + "text": "客户端\nclient", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "h5j5btibJLrw6noInTqeP", + "originalText": "客户端\nclient", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "U3ry809DLSs3z2kp6IH9m", + "type": "rectangle", + "x": 1500, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b18", + "roundness": { + "type": 3 + }, + "seed": 1347385919, + "version": 399, + "versionNonce": 942170245, + "isDeleted": false, + "boundElements": [ + { + "id": "YtfjI3Ub26xOmra4IKz1o", + "type": "text" + }, + { + "id": "0mjZSajWpy3Yilllikfkw", + "type": "arrow" + }, + { + "id": "UFwaLWIsE6V1u0wBoIki2", + "type": "arrow" + } + ], + "updated": 1742174887162, + "link": null, + "locked": false + }, + { + "id": "YtfjI3Ub26xOmra4IKz1o", + "type": "text", + "x": 1550.5, + "y": 1125, + "width": 99, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b19", + "roundness": null, + "seed": 1525460899, + "version": 387, + "versionNonce": 204528527, + "isDeleted": false, + "boundElements": [], + "updated": 1742547659072, + "link": null, + "locked": false, + "text": "白名单\nwhitelist", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "U3ry809DLSs3z2kp6IH9m", + "originalText": "白名单\nwhitelist", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "02tdc6VRdsQxJlfDZOFSa", + "type": "rectangle", + "x": 1500, + "y": 900, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1G", + "roundness": { + "type": 3 + }, + "seed": 1317855935, + "version": 695, + "versionNonce": 128501221, + "isDeleted": false, + "boundElements": [ + { + "id": "thsyIU4pVZ0BNyjfcbiI2", + "type": "text" + }, + { + "id": "SZvrwxNELkT_FwX9do5gG", + "type": "arrow" + }, + { + "id": "CZYYCHaD7DXcd4W6XNA--", + "type": "arrow" + }, + { + "id": "CS9A3vJgDw6jBX1HSPB-Y", + "type": "arrow" + }, + { + "id": "UFwaLWIsE6V1u0wBoIki2", + "type": "arrow" + } + ], + "updated": 1742174885226, + "link": null, + "locked": false + }, + { + "id": "thsyIU4pVZ0BNyjfcbiI2", + "type": "text", + "x": 1561.5, + "y": 925, + "width": 77, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1H", + "roundness": null, + "seed": 158171427, + "version": 658, + "versionNonce": 254689793, + "isDeleted": false, + "boundElements": [], + "updated": 1742547656056, + "link": null, + "locked": false, + "text": "通道\nchannel", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "02tdc6VRdsQxJlfDZOFSa", + "originalText": "通道\nchannel", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "-N71FG00rsWr4m7fJmQlh", + "type": "rectangle", + "x": 1800, + "y": 900, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1K", + "roundness": { + "type": 3 + }, + "seed": 232029951, + "version": 833, + "versionNonce": 1736773323, + "isDeleted": false, + "boundElements": [ + { + "id": "cKMWTO2RhuSj25E3cuaqQ", + "type": "text" + }, + { + "id": "CS9A3vJgDw6jBX1HSPB-Y", + "type": "arrow" + } + ], + "updated": 1742174886146, + "link": null, + "locked": false + }, + { + "id": "cKMWTO2RhuSj25E3cuaqQ", + "type": "text", + "x": 1878, + "y": 925, + "width": 44, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1L", + "roundness": null, + "seed": 392732365, + "version": 815, + "versionNonce": 1183996687, + "isDeleted": false, + "boundElements": [], + "updated": 1742547657496, + "link": null, + "locked": false, + "text": "节点\nnode", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "-N71FG00rsWr4m7fJmQlh", + "originalText": "节点\nnode", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "53l07ISb323iIFZb5LcXi", + "type": "rectangle", + "x": 1500, + "y": 600, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1M", + "roundness": { + "type": 3 + }, + "seed": 1913276191, + "version": 760, + "versionNonce": 922508933, + "isDeleted": false, + "boundElements": [ + { + "id": "-m-bUxtn95FYB5WDm-3ZL", + "type": "text" + }, + { + "id": "SZvrwxNELkT_FwX9do5gG", + "type": "arrow" + }, + { + "id": "YL-GQEx6_yX_IbTW-WC-i", + "type": "arrow" + }, + { + "id": "odssVsu1VEaRNn0teEV6c", + "type": "arrow" + } + ], + "updated": 1742174017442, + "link": null, + "locked": false + }, + { + "id": "-m-bUxtn95FYB5WDm-3ZL", + "type": "text", + "x": 1561.5, + "y": 625, + "width": 77, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1N", + "roundness": null, + "seed": 338615501, + "version": 816, + "versionNonce": 2001759457, + "isDeleted": false, + "boundElements": [], + "updated": 1742547642752, + "link": null, + "locked": false, + "text": "套餐\npackage", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "53l07ISb323iIFZb5LcXi", + "originalText": "套餐\npackage", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Bto4ODTDgZzQWKJot-An0", + "type": "rectangle", + "x": 1200, + "y": 400, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1O", + "roundness": { + "type": 3 + }, + "seed": 949095231, + "version": 596, + "versionNonce": 1449738508, + "isDeleted": false, + "boundElements": [ + { + "id": "xTn-xHdblzu6pXkpI7hur", + "type": "text" + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow" + }, + { + "id": "f8LDuzBxZAbowqqSWCw9G", + "type": "arrow" + }, + { + "id": "qFfFxWmqoOyrikrAZnpf6", + "type": "arrow" + } + ], + "updated": 1742290541387, + "link": null, + "locked": false + }, + { + "id": "xTn-xHdblzu6pXkpI7hur", + "type": "text", + "x": 1272.5, + "y": 425, + "width": 55, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1P", + "roundness": null, + "seed": 7953517, + "version": 579, + "versionNonce": 506951727, + "isDeleted": false, + "boundElements": [], + "updated": 1742547629912, + "link": null, + "locked": false, + "text": "订单\norder", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Bto4ODTDgZzQWKJot-An0", + "originalText": "订单\norder", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "1jrJPZuuyVEe_3htPZakP", + "type": "rectangle", + "x": 1200, + "y": 200, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1Q", + "roundness": { + "type": 3 + }, + "seed": 1036798815, + "version": 684, + "versionNonce": 1363126540, + "isDeleted": false, + "boundElements": [ + { + "id": "EaX-zjO0c6ukCwww1akJo", + "type": "text" + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow" + }, + { + "id": "2PC9t5zZFGDGscVW0UDJj", + "type": "arrow" + }, + { + "id": "qFfFxWmqoOyrikrAZnpf6", + "type": "arrow" + } + ], + "updated": 1742290543715, + "link": null, + "locked": false + }, + { + "id": "EaX-zjO0c6ukCwww1akJo", + "type": "text", + "x": 1278, + "y": 225, + "width": 44, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1R", + "roundness": null, + "seed": 1195160593, + "version": 705, + "versionNonce": 479957729, + "isDeleted": false, + "boundElements": [], + "updated": 1742547626824, + "link": null, + "locked": false, + "text": "账单\nbill", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1jrJPZuuyVEe_3htPZakP", + "originalText": "账单\nbill", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "o0zljZV7sfR-BL9k3f94Z", + "type": "rectangle", + "x": 1500, + "y": 400, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1RG", + "roundness": { + "type": 3 + }, + "seed": 2099894481, + "version": 737, + "versionNonce": 1348738383, + "isDeleted": false, + "boundElements": [ + { + "id": "3dVMCq4yFNWbC1kdkfaPf", + "type": "text" + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow" + }, + { + "id": "odssVsu1VEaRNn0teEV6c", + "type": "arrow" + }, + { + "id": "2PC9t5zZFGDGscVW0UDJj", + "type": "arrow" + } + ], + "updated": 1742547637469, + "link": null, + "locked": false + }, + { + "id": "3dVMCq4yFNWbC1kdkfaPf", + "type": "text", + "x": 1561.5, + "y": 425, + "width": 77, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1RV", + "roundness": null, + "seed": 630823103, + "version": 791, + "versionNonce": 598842223, + "isDeleted": false, + "boundElements": [], + "updated": 1742547637469, + "link": null, + "locked": false, + "text": "产品\nproduct", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "o0zljZV7sfR-BL9k3f94Z", + "originalText": "产品\nproduct", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Z-1rrNpR9cxTNRbZwOMzv", + "type": "rectangle", + "x": 900, + "y": 400, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1S", + "roundness": { + "type": 3 + }, + "seed": 2114553727, + "version": 573, + "versionNonce": 87346740, + "isDeleted": false, + "boundElements": [ + { + "id": "lZFuXHj8KTAOMiZPGUToB", + "type": "text" + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow" + }, + { + "id": "f8LDuzBxZAbowqqSWCw9G", + "type": "arrow" + } + ], + "updated": 1742290129959, + "link": null, + "locked": false + }, + { + "id": "lZFuXHj8KTAOMiZPGUToB", + "type": "text", + "x": 967, + "y": 425, + "width": 66, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1T", + "roundness": null, + "seed": 519839281, + "version": 578, + "versionNonce": 480371169, + "isDeleted": false, + "boundElements": [], + "updated": 1742547632352, + "link": null, + "locked": false, + "text": "退款\nrefund", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Z-1rrNpR9cxTNRbZwOMzv", + "originalText": "退款\nrefund", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "XTZyy4xXBotHDczHuQBlW", + "type": "rectangle", + "x": 900, + "y": 900, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1W", + "roundness": { + "type": 3 + }, + "seed": 209646527, + "version": 321, + "versionNonce": 240123531, + "isDeleted": false, + "boundElements": [ + { + "id": "nDYUnGUTxQMVU83SzHw2_", + "type": "text" + }, + { + "id": "X14BlJ5OoZr9srJ2jqOq0", + "type": "arrow" + }, + { + "id": "bYVMpDpG7ITLMefjWqJ3H", + "type": "arrow" + } + ], + "updated": 1742174882430, + "link": null, + "locked": false + }, + { + "id": "nDYUnGUTxQMVU83SzHw2_", + "type": "text", + "x": 950.5, + "y": 925, + "width": 99, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1X", + "roundness": null, + "seed": 76718787, + "version": 303, + "versionNonce": 246680193, + "isDeleted": false, + "boundElements": [], + "updated": 1742547654536, + "link": null, + "locked": false, + "text": "用户角色\nuser_role", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "XTZyy4xXBotHDczHuQBlW", + "originalText": "用户角色\nuser_role", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "gfY8zcITlvQxpk_76KC6v", + "type": "rectangle", + "x": 900, + "y": 1300, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1XG", + "roundness": { + "type": 3 + }, + "seed": 1873203493, + "version": 397, + "versionNonce": 1528157025, + "isDeleted": false, + "boundElements": [ + { + "id": "2L2lD_5SeLdB2P9cbGez5", + "type": "text" + }, + { + "id": "X14BlJ5OoZr9srJ2jqOq0", + "type": "arrow" + }, + { + "id": "bYVMpDpG7ITLMefjWqJ3H", + "type": "arrow" + }, + { + "id": "PgEvRszvDjVUltON9B_ah", + "type": "arrow" + }, + { + "id": "vZgnKG8BBOWQG2tFtJ1LQ", + "type": "arrow" + } + ], + "updated": 1742547785978, + "link": null, + "locked": false + }, + { + "id": "2L2lD_5SeLdB2P9cbGez5", + "type": "text", + "x": 945, + "y": 1325, + "width": 110, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1XV", + "roundness": null, + "seed": 1606811563, + "version": 404, + "versionNonce": 1587986241, + "isDeleted": false, + "boundElements": [], + "updated": 1742547785978, + "link": null, + "locked": false, + "text": "管理员角色\nadmin_role", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "gfY8zcITlvQxpk_76KC6v", + "originalText": "管理员角色\nadmin_role", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "4ThQqzkM9HN9O8tBl9SsM", + "type": "rectangle", + "x": 700, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1c", + "roundness": { + "type": 3 + }, + "seed": 1795815455, + "version": 505, + "versionNonce": 1642908033, + "isDeleted": false, + "boundElements": [ + { + "id": "fLEdgF1aVywlo7aJh2mxw", + "type": "text" + }, + { + "id": "bYVMpDpG7ITLMefjWqJ3H", + "type": "arrow" + }, + { + "id": "PgEvRszvDjVUltON9B_ah", + "type": "arrow" + }, + { + "id": "waO6Ar3IuQFLgZC7RgVw8", + "type": "arrow" + }, + { + "id": "RiId3-0t2GKp-zIkPXpSv", + "type": "arrow" + } + ], + "updated": 1742547852808, + "link": null, + "locked": false + }, + { + "id": "fLEdgF1aVywlo7aJh2mxw", + "type": "text", + "x": 745, + "y": 1125, + "width": 110, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1d", + "roundness": null, + "seed": 126885251, + "version": 489, + "versionNonce": 2004480353, + "isDeleted": false, + "boundElements": [], + "updated": 1742547852808, + "link": null, + "locked": false, + "text": "权限\npermission", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "4ThQqzkM9HN9O8tBl9SsM", + "originalText": "权限\npermission", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "dBnEj97v4n07GNk5DIfL0", + "type": "arrow", + "x": 1299.9999999980096, + "y": 899.5492546400992, + "width": 1.7673755792202428e-9, + "height": 399.0985092801986, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1e", + "roundness": { + "type": 2 + }, + "seed": 947100735, + "version": 1009, + "versionNonce": 297520268, + "isDeleted": false, + "boundElements": [], + "updated": 1742290541388, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 1.7673755792202428e-9, + -399.0985092801986 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "sWVhyKNTaf6c4MTpx5iEK", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "Bto4ODTDgZzQWKJot-An0", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "SZvrwxNELkT_FwX9do5gG", + "type": "arrow", + "x": 1599.9999967373865, + "y": 700.4507453599007, + "width": 0.00001294994103773206, + "height": 199.09850928019864, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1i", + "roundness": { + "type": 2 + }, + "seed": 445646015, + "version": 996, + "versionNonce": 180926245, + "isDeleted": false, + "boundElements": [], + "updated": 1742174885393, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.00001294994103773206, + 199.09850928019864 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "53l07ISb323iIFZb5LcXi", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "02tdc6VRdsQxJlfDZOFSa", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "CZYYCHaD7DXcd4W6XNA--", + "type": "arrow", + "x": 1406.07492398138, + "y": 950.360013516292, + "width": 87.8501520372406, + "height": 1.7817679327238238, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1j", + "roundness": { + "type": 2 + }, + "seed": 1892619487, + "version": 755, + "versionNonce": 1896665612, + "isDeleted": false, + "boundElements": [], + "updated": 1742290534477, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 87.8501520372406, + 1.7817679327238238 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "sWVhyKNTaf6c4MTpx5iEK", + "focus": -0.03443109298231502, + "gap": 6.88165624102885 + }, + "endBinding": { + "elementId": "02tdc6VRdsQxJlfDZOFSa", + "focus": -0.08251124351906901, + "gap": 6.88165624102794 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "CS9A3vJgDw6jBX1HSPB-Y", + "type": "arrow", + "x": 1799.1073474245104, + "y": 949.7125996559935, + "width": 98.21469484902082, + "height": 0.1952007666282043, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1k", + "roundness": { + "type": 2 + }, + "seed": 687284479, + "version": 1334, + "versionNonce": 1486529131, + "isDeleted": false, + "boundElements": [], + "updated": 1742174886326, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -98.21469484902082, + -0.1952007666282043 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "-N71FG00rsWr4m7fJmQlh", + "focus": 0.0017318665472098472, + "gap": 1 + }, + "endBinding": { + "elementId": "02tdc6VRdsQxJlfDZOFSa", + "focus": -0.013608399145615294, + "gap": 1 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "0mjZSajWpy3Yilllikfkw", + "type": "arrow", + "x": 1376.503084564913, + "y": 1001.7893826024881, + "width": 148.15785181545561, + "height": 96.67020783035014, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1l", + "roundness": { + "type": 2 + }, + "seed": 344500511, + "version": 812, + "versionNonce": 507424908, + "isDeleted": false, + "boundElements": [], + "updated": 1742290534477, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 148.15785181545561, + 96.67020783035014 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "sWVhyKNTaf6c4MTpx5iEK", + "focus": 0.016245380558013493, + "gap": 3.886186153256346 + }, + "endBinding": { + "elementId": "U3ry809DLSs3z2kp6IH9m", + "focus": 0.020677904364683856, + "gap": 3.3580310025523348 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "X14BlJ5OoZr9srJ2jqOq0", + "type": "arrow", + "x": 1193.9250760186205, + "y": 949.848464935628, + "width": 88.49772369630364, + "height": 2.5791852502960637, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1m", + "roundness": { + "type": 2 + }, + "seed": 16002367, + "version": 718, + "versionNonce": 917552908, + "isDeleted": false, + "boundElements": [], + "updated": 1742290534478, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -88.49772369630364, + 2.5791852502960637 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "sWVhyKNTaf6c4MTpx5iEK", + "focus": 0.061287490999459356, + "gap": 6.88165624102794 + }, + "endBinding": { + "elementId": "XTZyy4xXBotHDczHuQBlW", + "focus": 0.10395718271491888, + "gap": 6.139771544712403 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "bYVMpDpG7ITLMefjWqJ3H", + "type": "arrow", + "x": 949.5484070730799, + "y": 1000.4507453599007, + "width": 99.09795091921103, + "height": 99.09850928019853, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1n", + "roundness": { + "type": 2 + }, + "seed": 966405471, + "version": 1302, + "versionNonce": 676649281, + "isDeleted": false, + "boundElements": [], + "updated": 1742547853207, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -99.09795091921103, + 99.09850928019853 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "XTZyy4xXBotHDczHuQBlW", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "4ThQqzkM9HN9O8tBl9SsM", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "YL-GQEx6_yX_IbTW-WC-i", + "type": "arrow", + "x": 1387.4916965952257, + "y": 894.5241995657749, + "width": 135.34951954950702, + "height": 189.58704596901066, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1q", + "roundness": { + "type": 2 + }, + "seed": 2127012255, + "version": 727, + "versionNonce": 526459788, + "isDeleted": false, + "boundElements": [], + "updated": 1742290534476, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 135.34951954950702, + -189.58704596901066 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "sWVhyKNTaf6c4MTpx5iEK", + "focus": 0.352896122546675, + "gap": 11.470105293988343 + }, + "endBinding": { + "elementId": "53l07ISb323iIFZb5LcXi", + "focus": 0.27958203897203765, + "gap": 10.292853554707026 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "UFwaLWIsE6V1u0wBoIki2", + "type": "arrow", + "x": 1599.9991233067074, + "y": 1099.5492546400994, + "width": 0.0017209111779266095, + "height": 99.09850928019887, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1r", + "roundness": { + "type": 2 + }, + "seed": 700454335, + "version": 1081, + "versionNonce": 219276069, + "isDeleted": false, + "boundElements": [], + "updated": 1742174887260, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.0017209111779266095, + -99.09850928019887 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "U3ry809DLSs3z2kp6IH9m", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "02tdc6VRdsQxJlfDZOFSa", + "focus": 4.547473508864641e-15, + "gap": 1 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "f8LDuzBxZAbowqqSWCw9G", + "type": "arrow", + "x": 1199.1073474245106, + "y": 449.9940465457825, + "width": 98.21469484902127, + "height": 0.003553667229198254, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b1z", + "roundness": { + "type": 2 + }, + "seed": 1991877169, + "version": 533, + "versionNonce": 1948932876, + "isDeleted": false, + "boundElements": [], + "updated": 1742290541388, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -98.21469484902127, + -0.003553667229198254 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Bto4ODTDgZzQWKJot-An0", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "Z-1rrNpR9cxTNRbZwOMzv", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "qFfFxWmqoOyrikrAZnpf6", + "type": "arrow", + "x": 1300.0990604598142, + "y": 399.5491556175755, + "width": 0.19812091962853629, + "height": 99.09831123515096, + "angle": 0.0019992334198262185, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b20", + "roundness": { + "type": 2 + }, + "seed": 557170239, + "version": 499, + "versionNonce": 811557044, + "isDeleted": false, + "boundElements": [], + "updated": 1742290543717, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.19812091962853629, + -99.09831123515096 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Bto4ODTDgZzQWKJot-An0", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "1jrJPZuuyVEe_3htPZakP", + "focus": 4.547473508864641e-15, + "gap": 1 + }, + "startArrowhead": "crowfoot_one", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "2PC9t5zZFGDGscVW0UDJj", + "type": "arrow", + "x": 1524.5140286417316, + "y": 399.5492546400994, + "width": 148.77376423193277, + "height": 99.09850928019875, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b22", + "roundness": { + "type": 2 + }, + "seed": 2104703519, + "version": 338, + "versionNonce": 198966383, + "isDeleted": false, + "boundElements": [], + "updated": 1742547637519, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -148.77376423193277, + -99.09850928019875 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "o0zljZV7sfR-BL9k3f94Z", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "1jrJPZuuyVEe_3htPZakP", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "odssVsu1VEaRNn0teEV6c", + "type": "arrow", + "x": 1600.1911859049937, + "y": 500.45074535990057, + "width": 0.12668899335676542, + "height": 99.09850928019864, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b23", + "roundness": { + "type": 2 + }, + "seed": 350907519, + "version": 101, + "versionNonce": 1049009743, + "isDeleted": false, + "boundElements": [], + "updated": 1742547637519, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.12668899335676542, + 99.09850928019864 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "o0zljZV7sfR-BL9k3f94Z", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "53l07ISb323iIFZb5LcXi", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "PgEvRszvDjVUltON9B_ah", + "type": "arrow", + "x": 949.5484070730797, + "y": 1299.5492546400992, + "width": 99.09795091921092, + "height": 99.09850928019841, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b25", + "roundness": { + "type": 2 + }, + "seed": 50455717, + "version": 326, + "versionNonce": 499779873, + "isDeleted": false, + "boundElements": [], + "updated": 1742547853207, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -99.09795091921092, + -99.09850928019841 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "gfY8zcITlvQxpk_76KC6v", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "4ThQqzkM9HN9O8tBl9SsM", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "vZgnKG8BBOWQG2tFtJ1LQ", + "type": "arrow", + "x": 1199.1073474245104, + "y": 1351.6037029870622, + "width": 98.21469484902082, + "height": 1.7490579025375155, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b26", + "roundness": { + "type": 2 + }, + "seed": 468722923, + "version": 224, + "versionNonce": 348165825, + "isDeleted": false, + "boundElements": [], + "updated": 1742547785995, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -98.21469484902082, + 1.7490579025375155 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "6tM6EIcU9Lu7p7gOOmQwo", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "gfY8zcITlvQxpk_76KC6v", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "k8yWnHxYqT8tFxjwpm1CE", + "type": "arrow", + "x": 1300.0000000000002, + "y": 1299.5492546400994, + "width": 0, + "height": 299.09850928019875, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b27", + "roundness": { + "type": 2 + }, + "seed": 483548805, + "version": 60, + "versionNonce": 1049358753, + "isDeleted": false, + "boundElements": [], + "updated": 1742547783561, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0, + -299.09850928019875 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "6tM6EIcU9Lu7p7gOOmQwo", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "sWVhyKNTaf6c4MTpx5iEK", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "waO6Ar3IuQFLgZC7RgVw8", + "type": "arrow", + "x": 650.4498977928811, + "y": 1000.4507453599006, + "width": 99.09906764118648, + "height": 99.09850928019887, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b28", + "roundness": { + "type": 2 + }, + "seed": 775373088, + "version": 518, + "versionNonce": 818822401, + "isDeleted": false, + "boundElements": [], + "updated": 1742547853207, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 99.09906764118648, + 99.09850928019887 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "h5j5btibJLrw6noInTqeP", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "4ThQqzkM9HN9O8tBl9SsM", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "mKaBRuEfi2IsHiKLhkmSo", + "type": "rectangle", + "x": 1000, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b29", + "roundness": { + "type": 3 + }, + "seed": 1599772691, + "version": 369, + "versionNonce": 694500559, + "isDeleted": false, + "boundElements": [ + { + "id": "Mo8NPgGcHKL4d4B1GxZ5n", + "type": "text" + }, + { + "id": "42AGZYgRgHI8zEbq5zbGL", + "type": "arrow" + }, + { + "id": "RiId3-0t2GKp-zIkPXpSv", + "type": "arrow" + }, + { + "id": "vI7N6o6RZuvuZqC72CrG0", + "type": "arrow" + }, + { + "id": "ZGl1k8DptaKTTqI5hm8V3", + "type": "arrow" + } + ], + "updated": 1742547823272, + "link": null, + "locked": false + }, + { + "id": "Mo8NPgGcHKL4d4B1GxZ5n", + "type": "text", + "x": 1061.5, + "y": 1125, + "width": 77, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2A", + "roundness": null, + "seed": 387194291, + "version": 382, + "versionNonce": 661916513, + "isDeleted": false, + "boundElements": [], + "updated": 1742547810327, + "link": null, + "locked": false, + "text": "会话\nsession", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "mKaBRuEfi2IsHiKLhkmSo", + "originalText": "会话\nsession", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "42AGZYgRgHI8zEbq5zbGL", + "type": "arrow", + "x": 698.1537868311694, + "y": 989.2613984736022, + "width": 303.6928037196185, + "height": 121.47644031411915, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2B", + "roundness": { + "type": 2 + }, + "seed": 1515488609, + "version": 425, + "versionNonce": 728548527, + "isDeleted": false, + "boundElements": null, + "updated": 1742547850757, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 303.6928037196185, + 121.47644031411915 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "h5j5btibJLrw6noInTqeP", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "mKaBRuEfi2IsHiKLhkmSo", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "RiId3-0t2GKp-zIkPXpSv", + "type": "arrow", + "x": 900.8926525754894, + "y": 1150.0000000000045, + "width": 98.21469484902116, + "height": 2.7284841053187847e-12, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2C", + "roundness": { + "type": 2 + }, + "seed": 744247489, + "version": 191, + "versionNonce": 1625355489, + "isDeleted": false, + "boundElements": null, + "updated": 1742547853208, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 98.21469484902116, + -2.7284841053187847e-12 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "4ThQqzkM9HN9O8tBl9SsM", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "mKaBRuEfi2IsHiKLhkmSo", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "vI7N6o6RZuvuZqC72CrG0", + "type": "arrow", + "x": 1160, + "y": 1100, + "width": 80, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2D", + "roundness": { + "type": 2 + }, + "seed": 431825377, + "version": 19, + "versionNonce": 271078927, + "isDeleted": false, + "boundElements": null, + "updated": 1742547827591, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 80, + -100 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "mKaBRuEfi2IsHiKLhkmSo", + "focus": 0.1428571428571422, + "gap": 1 + }, + "endBinding": { + "elementId": "sWVhyKNTaf6c4MTpx5iEK", + "focus": 0.14285714285714268, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + }, + { + "id": "ZGl1k8DptaKTTqI5hm8V3", + "type": "arrow", + "x": 1160, + "y": 1200, + "width": 60, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2E", + "roundness": { + "type": 2 + }, + "seed": 1423978625, + "version": 12, + "versionNonce": 241355503, + "isDeleted": false, + "boundElements": null, + "updated": 1742547823273, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 60, + 100 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "mKaBRuEfi2IsHiKLhkmSo", + "focus": -0.23076923076923142, + "gap": 1 + }, + "endBinding": { + "elementId": "6tM6EIcU9Lu7p7gOOmQwo", + "focus": -0.3846153846153829, + "gap": 1 + }, + "startArrowhead": "crowfoot_many", + "endArrowhead": "crowfoot_many", + "elbowed": false + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": true, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/系统结构.excalidraw b/docs/系统结构.excalidraw new file mode 100644 index 0000000..8e4a279 --- /dev/null +++ b/docs/系统结构.excalidraw @@ -0,0 +1,1407 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "bG3OhbVvYxQq5u3Q8by6t", + "type": "rectangle", + "x": 800, + "y": 600, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2F", + "roundness": { + "type": 3 + }, + "seed": 437900214, + "version": 84, + "versionNonce": 1100241373, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "3zAcAt8chav-pDjV8o-hX" + }, + { + "id": "29Uhc0kMQz7XWFsz0ymfr", + "type": "arrow" + } + ], + "updated": 1742955008553, + "link": null, + "locked": false + }, + { + "id": "3zAcAt8chav-pDjV8o-hX", + "type": "text", + "x": 822.8000183105469, + "y": 620, + "width": 154.39996337890625, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2FV", + "roundness": null, + "seed": 1865252394, + "version": 216, + "versionNonce": 333217395, + "isDeleted": false, + "boundElements": [], + "updated": 1742955008553, + "link": null, + "locked": false, + "text": "platform\n用户接口\n登录|注册|购买|提取", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "bG3OhbVvYxQq5u3Q8by6t", + "originalText": "platform\n用户接口\n登录|注册|购买|提取", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "D82X4np2iMXmMYxAAIkij", + "type": "rectangle", + "x": 400, + "y": 600, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2FV8", + "roundness": { + "type": 3 + }, + "seed": 83811190, + "version": 194, + "versionNonce": 905496170, + "isDeleted": false, + "boundElements": [ + { + "id": "OiDd1a4qM63R1R45GHsLW", + "type": "text" + }, + { + "id": "29Uhc0kMQz7XWFsz0ymfr", + "type": "arrow" + } + ], + "updated": 1742954386337, + "link": null, + "locked": false + }, + { + "id": "OiDd1a4qM63R1R45GHsLW", + "type": "text", + "x": 478, + "y": 637.5, + "width": 44, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2FVG", + "roundness": null, + "seed": 1938243306, + "version": 198, + "versionNonce": 1218996522, + "isDeleted": false, + "boundElements": [], + "updated": 1742954386337, + "link": null, + "locked": false, + "text": "user", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "D82X4np2iMXmMYxAAIkij", + "originalText": "user", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "_tfec_zOn0Crg9mRklDpU", + "type": "rectangle", + "x": 800, + "y": 800, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2FVV", + "roundness": { + "type": 3 + }, + "seed": 679755306, + "version": 102, + "versionNonce": 84350525, + "isDeleted": false, + "boundElements": [ + { + "id": "DJ5mbodoRhKNKwyhXqyyr", + "type": "text" + }, + { + "id": "47Lyx8jEGynob7-oRG_PK", + "type": "arrow" + }, + { + "id": "UQXstwd69duz0k4sL4Q3h", + "type": "arrow" + } + ], + "updated": 1742955008553, + "link": null, + "locked": false + }, + { + "id": "DJ5mbodoRhKNKwyhXqyyr", + "type": "text", + "x": 828, + "y": 820, + "width": 144, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2FW", + "roundness": null, + "seed": 1065718134, + "version": 222, + "versionNonce": 55195667, + "isDeleted": false, + "boundElements": [], + "updated": 1742955008553, + "link": null, + "locked": false, + "text": "proxy\n代理服务,节点接口\n注册|鉴权|转发", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "_tfec_zOn0Crg9mRklDpU", + "originalText": "proxy\n代理服务,节点接口\n注册|鉴权|转发", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "stPY6KNLMkjWPxMQCIa6Z", + "type": "rectangle", + "x": 1100, + "y": 800, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2FW1", + "roundness": { + "type": 3 + }, + "seed": 503788138, + "version": 172, + "versionNonce": 1470318237, + "isDeleted": false, + "boundElements": [ + { + "id": "R8Vzf-BsuRIjDBKtAevEm", + "type": "text" + }, + { + "id": "47Lyx8jEGynob7-oRG_PK", + "type": "arrow" + }, + { + "id": "gE8J2ThSPgZtdS8qjQ_k6", + "type": "arrow" + }, + { + "id": "UQXstwd69duz0k4sL4Q3h", + "type": "arrow" + } + ], + "updated": 1742955008553, + "link": null, + "locked": false + }, + { + "id": "R8Vzf-BsuRIjDBKtAevEm", + "type": "text", + "x": 1152, + "y": 820, + "width": 96, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2FW2", + "roundness": null, + "seed": 252379446, + "version": 329, + "versionNonce": 1544535475, + "isDeleted": false, + "boundElements": [], + "updated": 1742955008553, + "link": null, + "locked": false, + "text": "wrapper\n业务协调\n外部节点处理", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "stPY6KNLMkjWPxMQCIa6Z", + "originalText": "wrapper\n业务协调\n外部节点处理", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "VF2h25xjhAYVlbfq3VEl0", + "type": "rectangle", + "x": 1100, + "y": 600, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2FW4", + "roundness": { + "type": 3 + }, + "seed": 985476406, + "version": 106, + "versionNonce": 2082106109, + "isDeleted": false, + "boundElements": [ + { + "id": "hWCgMPFIx799GS2Nau947", + "type": "text" + } + ], + "updated": 1742955008553, + "link": null, + "locked": false + }, + { + "id": "hWCgMPFIx799GS2Nau947", + "type": "text", + "x": 1131.6000061035156, + "y": 620, + "width": 136.79998779296875, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2FW8", + "roundness": null, + "seed": 1021307178, + "version": 257, + "versionNonce": 961777491, + "isDeleted": false, + "boundElements": [], + "updated": 1742955008553, + "link": null, + "locked": false, + "text": "tasks\n异步任务\n定时任务|离线任务", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "VF2h25xjhAYVlbfq3VEl0", + "originalText": "tasks\n异步任务\n定时任务|离线任务", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "cyMb2outgU_VzdMogQ4Qt", + "type": "rectangle", + "x": 400, + "y": 800, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2FWG", + "roundness": { + "type": 3 + }, + "seed": 1914175338, + "version": 236, + "versionNonce": 1338858090, + "isDeleted": false, + "boundElements": [ + { + "id": "9kPqsaZCsXRKDHVzjtF7U", + "type": "text" + }, + { + "id": "47Lyx8jEGynob7-oRG_PK", + "type": "arrow" + } + ], + "updated": 1742954455783, + "link": null, + "locked": false + }, + { + "id": "9kPqsaZCsXRKDHVzjtF7U", + "type": "text", + "x": 478, + "y": 837.5, + "width": 44, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2FWV", + "roundness": null, + "seed": 1526301750, + "version": 246, + "versionNonce": 1657437482, + "isDeleted": false, + "boundElements": [], + "updated": 1742954455783, + "link": null, + "locked": false, + "text": "node", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "cyMb2outgU_VzdMogQ4Qt", + "originalText": "node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "XVUCCNTIN4c6jG6UfQmsD", + "type": "rectangle", + "x": 1500, + "y": 800, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2FWd", + "roundness": { + "type": 3 + }, + "seed": 1502006646, + "version": 344, + "versionNonce": 716894198, + "isDeleted": false, + "boundElements": [ + { + "id": "1ckSj2i6DO9fGLo4L8StS", + "type": "text" + }, + { + "id": "47Lyx8jEGynob7-oRG_PK", + "type": "arrow" + }, + { + "id": "gE8J2ThSPgZtdS8qjQ_k6", + "type": "arrow" + } + ], + "updated": 1742954575893, + "link": null, + "locked": false + }, + { + "id": "1ckSj2i6DO9fGLo4L8StS", + "type": "text", + "x": 1572.5, + "y": 837.5, + "width": 55, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2FWl", + "roundness": null, + "seed": 19885290, + "version": 353, + "versionNonce": 151355702, + "isDeleted": false, + "boundElements": [], + "updated": 1742954575893, + "link": null, + "locked": false, + "text": "cloud", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "XVUCCNTIN4c6jG6UfQmsD", + "originalText": "cloud", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "wzg8-SWfds9xBN0GP3ygc", + "type": "rectangle", + "x": 1100, + "y": 300, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "Ph24pOzIAgb4e-lcZ5-2t", + "index": "b2FX", + "roundness": { + "type": 3 + }, + "seed": 131399658, + "version": 74, + "versionNonce": 1758994870, + "isDeleted": false, + "boundElements": [ + { + "id": "wDtYV4TyiYxkGZnAMrqzC", + "type": "text" + } + ], + "updated": 1742953923603, + "link": null, + "locked": false + }, + { + "id": "wDtYV4TyiYxkGZnAMrqzC", + "type": "text", + "x": 1164.800048828125, + "y": 330, + "width": 70.39990234375, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "Ph24pOzIAgb4e-lcZ5-2t", + "index": "b2FZ", + "roundness": null, + "seed": 1273678774, + "version": 101, + "versionNonce": 650618323, + "isDeleted": false, + "boundElements": [], + "updated": 1742955417694, + "link": null, + "locked": false, + "text": "postgres\n持久化", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "wzg8-SWfds9xBN0GP3ygc", + "originalText": "postgres\n持久化", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "xTt42xZCzANyc7oIwPw0D", + "type": "rectangle", + "x": 800, + "y": 300, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "Ph24pOzIAgb4e-lcZ5-2t", + "index": "b2Fa", + "roundness": { + "type": 3 + }, + "seed": 365534070, + "version": 82, + "versionNonce": 2014739190, + "isDeleted": false, + "boundElements": [ + { + "id": "IzUO1n4KU-OOm-FRwUNSb", + "type": "text" + } + ], + "updated": 1742953923603, + "link": null, + "locked": false + }, + { + "id": "IzUO1n4KU-OOm-FRwUNSb", + "type": "text", + "x": 847.6000061035156, + "y": 330, + "width": 104.79998779296875, + "height": 40, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "Ph24pOzIAgb4e-lcZ5-2t", + "index": "b2Fb", + "roundness": null, + "seed": 969441002, + "version": 140, + "versionNonce": 1388888477, + "isDeleted": false, + "boundElements": [], + "updated": 1742955419957, + "link": null, + "locked": false, + "text": "redis\n缓存|延迟队列", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "xTt42xZCzANyc7oIwPw0D", + "originalText": "redis\n缓存|延迟队列", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Ph24pOzIAgb4e-lcZ5-2t", + "type": "frame", + "x": 760, + "y": 260, + "width": 579.9999999999999, + "height": 180, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2H", + "roundness": null, + "seed": 931196394, + "version": 90, + "versionNonce": 1224281398, + "isDeleted": false, + "boundElements": [], + "updated": 1742953923294, + "link": null, + "locked": false, + "name": "基础设施" + }, + { + "id": "UQXstwd69duz0k4sL4Q3h", + "type": "arrow", + "x": 1000.8926525754894, + "y": 850.0000058406345, + "width": 98.21469484902104, + "height": 0.000002860056270037603, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2HV", + "roundness": { + "type": 2 + }, + "seed": 49992125, + "version": 157, + "versionNonce": 1659765597, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "dytyaDfWPBcWMncxOmB4U" + } + ], + "updated": 1742955008553, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 98.21469484902104, + -0.000002860056270037603 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "_tfec_zOn0Crg9mRklDpU", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "stPY6KNLMkjWPxMQCIa6Z", + "focus": 0, + "gap": 1 + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "dytyaDfWPBcWMncxOmB4U", + "type": "text", + "x": 1034, + "y": 840.0000044106064, + "width": 32, + "height": 20, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "GonigfB1XFbG2WnNlEmb8", + "index": "b2Hl", + "roundness": null, + "seed": 482695731, + "version": 22, + "versionNonce": 225193203, + "isDeleted": false, + "boundElements": null, + "updated": 1742955008553, + "link": null, + "locked": false, + "text": "合并", + "fontSize": 16, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UQXstwd69duz0k4sL4Q3h", + "originalText": "合并", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "GonigfB1XFbG2WnNlEmb8", + "type": "frame", + "x": 760, + "y": 560, + "width": 580, + "height": 380, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2I", + "roundness": null, + "seed": 521293942, + "version": 38, + "versionNonce": 2027320221, + "isDeleted": false, + "boundElements": [], + "updated": 1742955008252, + "link": null, + "locked": false, + "name": "服务" + }, + { + "id": "47Lyx8jEGynob7-oRG_PK", + "type": "arrow", + "x": 600.8926525754895, + "y": 848.2983823002315, + "width": 198.21469484902093, + "height": 1.127640842143137, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2J", + "roundness": { + "type": 2 + }, + "seed": 1215048950, + "version": 332, + "versionNonce": 1178122922, + "isDeleted": false, + "boundElements": [], + "updated": 1742954455800, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 198.21469484902093, + 1.127640842143137 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "cyMb2outgU_VzdMogQ4Qt", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "_tfec_zOn0Crg9mRklDpU", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "29Uhc0kMQz7XWFsz0ymfr", + "type": "arrow", + "x": 600.8926525754895, + "y": 650.0000450491988, + "width": 198.21469484902104, + "height": 0.00002759751839676028, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2M", + "roundness": { + "type": 2 + }, + "seed": 1615242602, + "version": 52, + "versionNonce": 1063009386, + "isDeleted": false, + "boundElements": [], + "updated": 1742954386553, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 198.21469484902104, + -0.00002759751839676028 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "D82X4np2iMXmMYxAAIkij", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "bG3OhbVvYxQq5u3Q8by6t", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "gE8J2ThSPgZtdS8qjQ_k6", + "type": "arrow", + "x": 1300.8926525754898, + "y": 850.0000000008117, + "width": 198.2146948490206, + "height": 1.594685272721108e-9, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2N", + "roundness": { + "type": 2 + }, + "seed": 2140865974, + "version": 316, + "versionNonce": 1948666803, + "isDeleted": false, + "boundElements": [], + "updated": 1742954692340, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 198.2146948490206, + 1.594685272721108e-9 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "stPY6KNLMkjWPxMQCIa6Z", + "focus": 0, + "gap": 1 + }, + "endBinding": { + "elementId": "XVUCCNTIN4c6jG6UfQmsD", + "focus": 0, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "BTk-oSNiNVQ40B5SwH_7C", + "type": "rectangle", + "x": 400, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2O", + "roundness": { + "type": 3 + }, + "seed": 621758781, + "version": 223, + "versionNonce": 1785861661, + "isDeleted": false, + "boundElements": [ + { + "id": "_TXdhvPuy6k3ZQWgPMcTT", + "type": "text" + } + ], + "updated": 1742955138327, + "link": null, + "locked": false + }, + { + "id": "_TXdhvPuy6k3ZQWgPMcTT", + "type": "text", + "x": 483.5, + "y": 1137.5, + "width": 33, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2P", + "roundness": null, + "seed": 1311578525, + "version": 282, + "versionNonce": 1225552723, + "isDeleted": false, + "boundElements": [], + "updated": 1742955298430, + "link": null, + "locked": false, + "text": "env", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BTk-oSNiNVQ40B5SwH_7C", + "originalText": "env", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "M4Xt6FejXNGres-729aAR", + "type": "rectangle", + "x": 1000, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2Q", + "roundness": { + "type": 3 + }, + "seed": 1207033341, + "version": 178, + "versionNonce": 2070104701, + "isDeleted": false, + "boundElements": [ + { + "id": "ZbcO6wndlv4gaMM459d3V", + "type": "text" + } + ], + "updated": 1742955138327, + "link": null, + "locked": false + }, + { + "id": "ZbcO6wndlv4gaMM459d3V", + "type": "text", + "x": 1083.5, + "y": 1137.5, + "width": 33, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2R", + "roundness": null, + "seed": 1230968413, + "version": 265, + "versionNonce": 425598067, + "isDeleted": false, + "boundElements": [], + "updated": 1742955303965, + "link": null, + "locked": false, + "text": "orm", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "M4Xt6FejXNGres-729aAR", + "originalText": "orm", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "_nGlpGbBalaisNjfuxEf3", + "type": "frame", + "x": 360, + "y": 1060, + "width": 1480, + "height": 180, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2S", + "roundness": null, + "seed": 759579325, + "version": 318, + "versionNonce": 1442996189, + "isDeleted": false, + "boundElements": [], + "updated": 1742955138080, + "link": null, + "locked": false, + "name": "依赖库" + }, + { + "id": "kYmlz4qe4AIP8D4bkHB2i", + "type": "rectangle", + "x": 1600, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2T", + "roundness": { + "type": 3 + }, + "seed": 1396728669, + "version": 235, + "versionNonce": 1424945885, + "isDeleted": false, + "boundElements": [ + { + "id": "9vSlZhHxrOmmmaiNwqu1a", + "type": "text" + } + ], + "updated": 1742955138327, + "link": null, + "locked": false + }, + { + "id": "9vSlZhHxrOmmmaiNwqu1a", + "type": "text", + "x": 1667, + "y": 1137.5, + "width": 66, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2U", + "roundness": null, + "seed": 1735782333, + "version": 267, + "versionNonce": 705423571, + "isDeleted": false, + "boundElements": [], + "updated": 1742955308562, + "link": null, + "locked": false, + "text": "remote", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kYmlz4qe4AIP8D4bkHB2i", + "originalText": "remote", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "tVQcr9aHB8FsvH_YT3oIl", + "type": "rectangle", + "x": 700, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2V", + "roundness": { + "type": 3 + }, + "seed": 1370628947, + "version": 222, + "versionNonce": 1671860029, + "isDeleted": false, + "boundElements": [ + { + "id": "G4mCgnAS-6MWuHyiAwcZX", + "type": "text" + } + ], + "updated": 1742955138327, + "link": null, + "locked": false + }, + { + "id": "G4mCgnAS-6MWuHyiAwcZX", + "type": "text", + "x": 778, + "y": 1137.5, + "width": 44, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2W", + "roundness": null, + "seed": 1953673053, + "version": 312, + "versionNonce": 1365376691, + "isDeleted": false, + "boundElements": null, + "updated": 1742955301710, + "link": null, + "locked": false, + "text": "logs", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "tVQcr9aHB8FsvH_YT3oIl", + "originalText": "logs", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "cAuyR1X_Ccd16T2TEAumz", + "type": "rectangle", + "x": 1300, + "y": 1100, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2X", + "roundness": { + "type": 3 + }, + "seed": 1847838771, + "version": 282, + "versionNonce": 99327901, + "isDeleted": false, + "boundElements": [ + { + "id": "2CnXN8Z8CSVQ1NVq1Qeyq", + "type": "text" + } + ], + "updated": 1742955138327, + "link": null, + "locked": false + }, + { + "id": "2CnXN8Z8CSVQ1NVq1Qeyq", + "type": "text", + "x": 1383.5, + "y": 1137.5, + "width": 33, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "_nGlpGbBalaisNjfuxEf3", + "index": "b2Y", + "roundness": null, + "seed": 1291995261, + "version": 309, + "versionNonce": 1050208819, + "isDeleted": false, + "boundElements": null, + "updated": 1742955306101, + "link": null, + "locked": false, + "text": "rds", + "fontSize": 20, + "fontFamily": 8, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "cAuyR1X_Ccd16T2TEAumz", + "originalText": "rds", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": true, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/go.mod b/go.mod index a56cc3c..4f18c78 100644 --- a/go.mod +++ b/go.mod @@ -7,9 +7,9 @@ require ( github.com/gofiber/fiber/v2 v2.52.6 github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 + github.com/jxskiss/base62 v1.1.0 github.com/lmittmann/tint v1.0.7 github.com/redis/go-redis/v9 v9.3.0 - github.com/stretchr/testify v1.8.1 golang.org/x/crypto v0.17.0 gorm.io/driver/postgres v1.5.11 gorm.io/gen v0.3.26 @@ -21,7 +21,6 @@ require ( github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -31,13 +30,10 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.17.9 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect @@ -47,7 +43,6 @@ require ( golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.23.0 // indirect golang.org/x/tools v0.26.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect gorm.io/driver/mysql v1.5.7 // indirect gorm.io/hints v1.1.0 // indirect diff --git a/go.sum b/go.sum index 010c07b..e192c4f 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -41,12 +40,10 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= +github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lmittmann/tint v1.0.7 h1:D/0OqWZ0YOGZ6AyC+5Y2kD8PBEzBk6rFHVSfOqCkF9Y= github.com/lmittmann/tint v1.0.7/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -67,15 +64,9 @@ github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0 github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -132,8 +123,6 @@ golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/init/env/env.go b/pkg/env/env.go similarity index 100% rename from init/env/env.go rename to pkg/env/env.go diff --git a/init/logs/logs.go b/pkg/logs/logs.go similarity index 95% rename from init/logs/logs.go rename to pkg/logs/logs.go index 74ae61b..fbc2948 100644 --- a/init/logs/logs.go +++ b/pkg/logs/logs.go @@ -3,7 +3,7 @@ package logs import ( "log/slog" "os" - "platform/init/env" + "platform/pkg/env" "time" "github.com/lmittmann/tint" diff --git a/init/orm/orm.go b/pkg/orm/orm.go similarity index 97% rename from init/orm/orm.go rename to pkg/orm/orm.go index a62016e..df6175a 100644 --- a/init/orm/orm.go +++ b/pkg/orm/orm.go @@ -3,7 +3,7 @@ package orm import ( "fmt" "log/slog" - "platform/init/env" + "platform/pkg/env" "platform/web/queries" "gorm.io/gorm" diff --git a/init/rds/rds.go b/pkg/rds/rds.go similarity index 92% rename from init/rds/rds.go rename to pkg/rds/rds.go index 4b92615..b3eb3f1 100644 --- a/init/rds/rds.go +++ b/pkg/rds/rds.go @@ -2,7 +2,7 @@ package rds import ( "net" - "platform/init/env" + "platform/pkg/env" "github.com/redis/go-redis/v9" ) diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go new file mode 100644 index 0000000..9d57881 --- /dev/null +++ b/pkg/remote/remote.go @@ -0,0 +1,126 @@ +package remote + +import ( + "encoding/json" + "errors" + "io" + "net/http" + "strings" +) + +type client struct { + gatewayUrl string + username string + password string + cloudUrl string + token string +} + +var Client client + +func Init() error { + // todo 从环境变量中获取参数 + Client = client{ + gatewayUrl: "http://110.40.82.248:9990", + username: "api", + password: "123456", + cloudUrl: "http://103.139.212.110", + } + + return nil +} + +type PortConfig struct { + Port string `json:"port"` + Edge []string `json:"edge"` + Type string `json:"type"` + Time int `json:"time"` + Status bool `json:"status"` + Rate int `json:"rate"` + Whitelist []string `json:"whitelist"` + Userpass string `json:"userpass"` + AutoEdgeConfig AutoEdgeConfig `json:"auto_edge_config"` +} + +type AutoEdgeConfig struct { + Province string `json:"province"` + City string `json:"city"` + Isp string `json:"isp"` + Count int `json:"count"` + PacketLoss int `json:"packet_loss"` +} + +func (c *client) PortConfigs(params ...PortConfig) error { + resp, err := c.requestCloud("/port/configs", params) + if err != nil { + return err + } + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + if resp.StatusCode != http.StatusOK { + return errors.New("failed to configure port") + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + var result map[string]any + err = json.Unmarshal(body, &result) + if err != nil { + return err + } + + if result["code"] != 0 { + return errors.New("failed to configure port") + } + + return nil +} + +func (c *client) requestGateway(url string, data any) (*http.Response, error) { + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", c.gatewayUrl+url, strings.NewReader(string(jsonData))) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.SetBasicAuth(c.username, c.password) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (c *client) requestCloud(url string, data any) (*http.Response, error) { + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", c.cloudUrl+url, strings.NewReader(string(jsonData))) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("token", c.token) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/scripts/sql/init.sql b/scripts/sql/init.sql index b88b89b..a527ba1 100644 --- a/scripts/sql/init.sql +++ b/scripts/sql/init.sql @@ -516,16 +516,19 @@ create table resource ( user_id int not null references "user" (id) on update cascade on delete cascade, + active bool default true, created_at timestamp default current_timestamp, updated_at timestamp default current_timestamp, deleted_at timestamp ); create index resource_user_id_index on resource (user_id); +create index resource_active_index on resource (active); -- resource表字段注释 comment on table resource is '套餐表'; comment on column resource.id is '套餐ID'; comment on column resource.user_id is '用户ID'; +comment on column resource.active is '套餐状态'; comment on column resource.created_at is '创建时间'; comment on column resource.updated_at is '更新时间'; comment on column resource.deleted_at is '删除时间'; @@ -534,19 +537,19 @@ comment on column resource.deleted_at is '删除时间'; drop table if exists resource_pss cascade; create table resource_pss ( id serial primary key, - resource_id int not null references resource (id) + resource_id int not null references resource (id) on update cascade on delete cascade, - active bool not null default false, type int, live int, quota int, used int, expire timestamp, - limit_day int, - last_used timestamp, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp, + daily_limit int, + daily_used int, + daily_last timestamp, + created_at timestamp default current_timestamp, + updated_at timestamp default current_timestamp, deleted_at timestamp ); create index resource_pss_resource_id_index on resource_pss (resource_id); @@ -555,14 +558,14 @@ create index resource_pss_resource_id_index on resource_pss (resource_id); comment on table resource_pss is '动态代理套餐表'; comment on column resource_pss.id is 'ID'; comment on column resource_pss.resource_id is '套餐ID'; -comment on column resource_pss.active is '是否启用'; comment on column resource_pss.type is '套餐类型:1-包时,2-包量'; comment on column resource_pss.live is '可用时长(秒)'; comment on column resource_pss.quota is '配额数量'; comment on column resource_pss.used is '已用数量'; comment on column resource_pss.expire is '过期时间'; -comment on column resource_pss.limit_day is '每日限额'; -comment on column resource_pss.last_used is '最后提取时间'; +comment on column resource_pss.daily_limit is '每日限制'; +comment on column resource_pss.daily_used is '今日已用数量'; +comment on column resource_pss.daily_last is '今日最后使用时间'; comment on column resource_pss.created_at is '创建时间'; comment on column resource_pss.updated_at is '更新时间'; comment on column resource_pss.deleted_at is '删除时间'; @@ -571,16 +574,15 @@ comment on column resource_pss.deleted_at is '删除时间'; drop table if exists resource_psr cascade; create table resource_psr ( id serial primary key, - resource_id int not null references resource (id) + resource_id int not null references resource (id) on update cascade on delete cascade, - active bool not null default false, live int, conn int, expire timestamp, used bool, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp, + created_at timestamp default current_timestamp, + updated_at timestamp default current_timestamp, deleted_at timestamp ); create index resource_psr_resource_id_index on resource_psr (resource_id); @@ -589,7 +591,6 @@ create index resource_psr_resource_id_index on resource_psr (resource_id); comment on table resource_psr is '隧道代理套餐表'; comment on column resource_psr.id is 'ID'; comment on column resource_psr.resource_id is '套餐ID'; -comment on column resource_psr.active is '是否启用'; comment on column resource_psr.live is '轮换周期(秒)'; comment on column resource_psr.conn is '最大连接数'; comment on column resource_psr.expire is '过期时间'; @@ -602,12 +603,11 @@ comment on column resource_psr.deleted_at is '删除时间'; drop table if exists resource_pps cascade; create table resource_pps ( id serial primary key, - resource_id int not null references resource (id) + resource_id int not null references resource (id) on update cascade on delete cascade, - active bool not null default false, - created_at timestamp default current_timestamp, - updated_at timestamp default current_timestamp, + created_at timestamp default current_timestamp, + updated_at timestamp default current_timestamp, deleted_at timestamp ); create index resource_pps_resource_id_index on resource_pps (resource_id); @@ -616,7 +616,6 @@ create index resource_pps_resource_id_index on resource_pps (resource_id); comment on table resource_pps is '独享代理套餐表'; comment on column resource_pps.id is 'ID'; comment on column resource_pps.resource_id is '套餐ID'; -comment on column resource_pps.active is '是否启用'; comment on column resource_pps.created_at is '创建时间'; comment on column resource_pps.updated_at is '更新时间'; comment on column resource_pps.deleted_at is '删除时间'; diff --git a/web/auth.go b/web/auth.go index 9c50676..a2bc900 100644 --- a/web/auth.go +++ b/web/auth.go @@ -9,8 +9,8 @@ import ( "github.com/gofiber/fiber/v2" ) -// Protect 创建针对单个路由的鉴权中间件 -func Protect(permissions ...string) fiber.Handler { +// PermitUser 创建针对单个路由的鉴权中间件 +func PermitUser(permissions ...string) fiber.Handler { return func(c *fiber.Ctx) error { // 获取令牌 var header = c.Get("Authorization") @@ -32,7 +32,17 @@ func Protect(permissions ...string) fiber.Handler { } // 检查权限 - if len(permissions) > 0 && !auth.AnyPermission(permissions...) { + switch auth.Payload.Type { + case services.PayloadAdmin: + // 管理员不需要权限检查 + case services.PayloadUser: + if len(permissions) > 0 && !auth.AnyPermission(permissions...) { + return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{ + Error: true, + Message: "拒绝访问", + }) + } + default: return c.Status(fiber.StatusForbidden).JSON(common.ErrResp{ Error: true, Message: "拒绝访问", diff --git a/web/common/errors.go b/web/common/errors.go new file mode 100644 index 0000000..6bd3752 --- /dev/null +++ b/web/common/errors.go @@ -0,0 +1,13 @@ +package common + +type AuthUnAuthorizedErr string + +func (e AuthUnAuthorizedErr) Error() string { + return string(e) +} + +type AuthForbiddenErr string + +func (e AuthForbiddenErr) Error() string { + return string(e) +} diff --git a/web/handlers/channel.go b/web/handlers/channel.go index c5df6f9..3bac3d7 100644 --- a/web/handlers/channel.go +++ b/web/handlers/channel.go @@ -14,7 +14,7 @@ import ( type CreateChannelReq struct { Region string `json:"region" validate:"required"` Provider string `json:"provider" validate:"required"` - Protocol services.Protocol `json:"protocol" validate:"required,oneof=socks5 http https"` + Protocol services.ChannelProtocol `json:"protocol" validate:"required,oneof=socks5 http https"` ResourceId int `json:"resource_id" validate:"required"` Count int `json:"count" validate:"required"` ResultType CreateChannelResultType `json:"return_type" validate:"required,oneof=json text"` diff --git a/web/models/resource.gen.go b/web/models/resource.gen.go index aa982b1..437eb71 100644 --- a/web/models/resource.gen.go +++ b/web/models/resource.gen.go @@ -16,6 +16,7 @@ const TableNameResource = "resource" type Resource struct { ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:套餐ID" json:"id"` // 套餐ID UserID int32 `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID + Active bool `gorm:"column:active;default:true;comment:套餐状态" json:"active"` // 套餐状态 CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 diff --git a/web/models/resource_pps.gen.go b/web/models/resource_pps.gen.go index a7e9b4d..b8dee0d 100644 --- a/web/models/resource_pps.gen.go +++ b/web/models/resource_pps.gen.go @@ -16,7 +16,6 @@ const TableNameResourcePps = "resource_pps" type ResourcePps struct { ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID - Active bool `gorm:"column:active;not null;comment:是否启用" json:"active"` // 是否启用 CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 diff --git a/web/models/resource_psr.gen.go b/web/models/resource_psr.gen.go index 2d0ba44..5e75609 100644 --- a/web/models/resource_psr.gen.go +++ b/web/models/resource_psr.gen.go @@ -16,7 +16,6 @@ const TableNameResourcePsr = "resource_psr" type ResourcePsr struct { ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID - Active bool `gorm:"column:active;not null;comment:是否启用" json:"active"` // 是否启用 Live int32 `gorm:"column:live;comment:轮换周期(秒)" json:"live"` // 轮换周期(秒) Conn int32 `gorm:"column:conn;comment:最大连接数" json:"conn"` // 最大连接数 Expire time.Time `gorm:"column:expire;comment:过期时间" json:"expire"` // 过期时间 diff --git a/web/models/resource_pss.gen.go b/web/models/resource_pss.gen.go index c968dcd..a681863 100644 --- a/web/models/resource_pss.gen.go +++ b/web/models/resource_pss.gen.go @@ -16,14 +16,14 @@ const TableNameResourcePss = "resource_pss" type ResourcePss struct { ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:ID" json:"id"` // ID ResourceID int32 `gorm:"column:resource_id;not null;comment:套餐ID" json:"resource_id"` // 套餐ID - Active bool `gorm:"column:active;not null;comment:是否启用" json:"active"` // 是否启用 Type int32 `gorm:"column:type;comment:套餐类型:1-包时,2-包量" json:"type"` // 套餐类型:1-包时,2-包量 Live int32 `gorm:"column:live;comment:可用时长(秒)" json:"live"` // 可用时长(秒) Quota int32 `gorm:"column:quota;comment:配额数量" json:"quota"` // 配额数量 Used int32 `gorm:"column:used;comment:已用数量" json:"used"` // 已用数量 Expire time.Time `gorm:"column:expire;comment:过期时间" json:"expire"` // 过期时间 - LimitDay int32 `gorm:"column:limit_day;comment:每日限额" json:"limit_day"` // 每日限额 - LastUsed time.Time `gorm:"column:last_used;comment:最后提取时间" json:"last_used"` // 最后提取时间 + DailyLimit int32 `gorm:"column:daily_limit;comment:每日限制" json:"daily_limit"` // 每日限制 + DailyUsed int32 `gorm:"column:daily_used;comment:今日已用数量" json:"daily_used"` // 今日已用数量 + DailyLast time.Time `gorm:"column:daily_last;comment:今日最后使用时间" json:"daily_last"` // 今日最后使用时间 CreatedAt time.Time `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 UpdatedAt time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 diff --git a/web/queries/resource.gen.go b/web/queries/resource.gen.go index c443314..7adcd18 100644 --- a/web/queries/resource.gen.go +++ b/web/queries/resource.gen.go @@ -29,6 +29,7 @@ func newResource(db *gorm.DB, opts ...gen.DOOption) resource { _resource.ALL = field.NewAsterisk(tableName) _resource.ID = field.NewInt32(tableName, "id") _resource.UserID = field.NewInt32(tableName, "user_id") + _resource.Active = field.NewBool(tableName, "active") _resource.CreatedAt = field.NewTime(tableName, "created_at") _resource.UpdatedAt = field.NewTime(tableName, "updated_at") _resource.DeletedAt = field.NewField(tableName, "deleted_at") @@ -44,6 +45,7 @@ type resource struct { ALL field.Asterisk ID field.Int32 // 套餐ID UserID field.Int32 // 用户ID + Active field.Bool // 套餐状态 CreatedAt field.Time // 创建时间 UpdatedAt field.Time // 更新时间 DeletedAt field.Field // 删除时间 @@ -65,6 +67,7 @@ func (r *resource) updateTableName(table string) *resource { r.ALL = field.NewAsterisk(table) r.ID = field.NewInt32(table, "id") r.UserID = field.NewInt32(table, "user_id") + r.Active = field.NewBool(table, "active") r.CreatedAt = field.NewTime(table, "created_at") r.UpdatedAt = field.NewTime(table, "updated_at") r.DeletedAt = field.NewField(table, "deleted_at") @@ -84,9 +87,10 @@ func (r *resource) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (r *resource) fillFieldMap() { - r.fieldMap = make(map[string]field.Expr, 5) + r.fieldMap = make(map[string]field.Expr, 6) r.fieldMap["id"] = r.ID r.fieldMap["user_id"] = r.UserID + r.fieldMap["active"] = r.Active r.fieldMap["created_at"] = r.CreatedAt r.fieldMap["updated_at"] = r.UpdatedAt r.fieldMap["deleted_at"] = r.DeletedAt diff --git a/web/queries/resource_pps.gen.go b/web/queries/resource_pps.gen.go index 0b59c89..a1f81ea 100644 --- a/web/queries/resource_pps.gen.go +++ b/web/queries/resource_pps.gen.go @@ -29,7 +29,6 @@ func newResourcePps(db *gorm.DB, opts ...gen.DOOption) resourcePps { _resourcePps.ALL = field.NewAsterisk(tableName) _resourcePps.ID = field.NewInt32(tableName, "id") _resourcePps.ResourceID = field.NewInt32(tableName, "resource_id") - _resourcePps.Active = field.NewBool(tableName, "active") _resourcePps.CreatedAt = field.NewTime(tableName, "created_at") _resourcePps.UpdatedAt = field.NewTime(tableName, "updated_at") _resourcePps.DeletedAt = field.NewField(tableName, "deleted_at") @@ -45,7 +44,6 @@ type resourcePps struct { ALL field.Asterisk ID field.Int32 // ID ResourceID field.Int32 // 套餐ID - Active field.Bool // 是否启用 CreatedAt field.Time // 创建时间 UpdatedAt field.Time // 更新时间 DeletedAt field.Field // 删除时间 @@ -67,7 +65,6 @@ func (r *resourcePps) updateTableName(table string) *resourcePps { r.ALL = field.NewAsterisk(table) r.ID = field.NewInt32(table, "id") r.ResourceID = field.NewInt32(table, "resource_id") - r.Active = field.NewBool(table, "active") r.CreatedAt = field.NewTime(table, "created_at") r.UpdatedAt = field.NewTime(table, "updated_at") r.DeletedAt = field.NewField(table, "deleted_at") @@ -87,10 +84,9 @@ func (r *resourcePps) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (r *resourcePps) fillFieldMap() { - r.fieldMap = make(map[string]field.Expr, 6) + r.fieldMap = make(map[string]field.Expr, 5) r.fieldMap["id"] = r.ID r.fieldMap["resource_id"] = r.ResourceID - r.fieldMap["active"] = r.Active r.fieldMap["created_at"] = r.CreatedAt r.fieldMap["updated_at"] = r.UpdatedAt r.fieldMap["deleted_at"] = r.DeletedAt diff --git a/web/queries/resource_psr.gen.go b/web/queries/resource_psr.gen.go index adaade5..a1b752c 100644 --- a/web/queries/resource_psr.gen.go +++ b/web/queries/resource_psr.gen.go @@ -29,7 +29,6 @@ func newResourcePsr(db *gorm.DB, opts ...gen.DOOption) resourcePsr { _resourcePsr.ALL = field.NewAsterisk(tableName) _resourcePsr.ID = field.NewInt32(tableName, "id") _resourcePsr.ResourceID = field.NewInt32(tableName, "resource_id") - _resourcePsr.Active = field.NewBool(tableName, "active") _resourcePsr.Live = field.NewInt32(tableName, "live") _resourcePsr.Conn = field.NewInt32(tableName, "conn") _resourcePsr.Expire = field.NewTime(tableName, "expire") @@ -49,7 +48,6 @@ type resourcePsr struct { ALL field.Asterisk ID field.Int32 // ID ResourceID field.Int32 // 套餐ID - Active field.Bool // 是否启用 Live field.Int32 // 轮换周期(秒) Conn field.Int32 // 最大连接数 Expire field.Time // 过期时间 @@ -75,7 +73,6 @@ func (r *resourcePsr) updateTableName(table string) *resourcePsr { r.ALL = field.NewAsterisk(table) r.ID = field.NewInt32(table, "id") r.ResourceID = field.NewInt32(table, "resource_id") - r.Active = field.NewBool(table, "active") r.Live = field.NewInt32(table, "live") r.Conn = field.NewInt32(table, "conn") r.Expire = field.NewTime(table, "expire") @@ -99,10 +96,9 @@ func (r *resourcePsr) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (r *resourcePsr) fillFieldMap() { - r.fieldMap = make(map[string]field.Expr, 10) + r.fieldMap = make(map[string]field.Expr, 9) r.fieldMap["id"] = r.ID r.fieldMap["resource_id"] = r.ResourceID - r.fieldMap["active"] = r.Active r.fieldMap["live"] = r.Live r.fieldMap["conn"] = r.Conn r.fieldMap["expire"] = r.Expire diff --git a/web/queries/resource_pss.gen.go b/web/queries/resource_pss.gen.go index 7d92b04..2583bb2 100644 --- a/web/queries/resource_pss.gen.go +++ b/web/queries/resource_pss.gen.go @@ -29,14 +29,14 @@ func newResourcePss(db *gorm.DB, opts ...gen.DOOption) resourcePss { _resourcePss.ALL = field.NewAsterisk(tableName) _resourcePss.ID = field.NewInt32(tableName, "id") _resourcePss.ResourceID = field.NewInt32(tableName, "resource_id") - _resourcePss.Active = field.NewBool(tableName, "active") _resourcePss.Type = field.NewInt32(tableName, "type") _resourcePss.Live = field.NewInt32(tableName, "live") _resourcePss.Quota = field.NewInt32(tableName, "quota") _resourcePss.Used = field.NewInt32(tableName, "used") _resourcePss.Expire = field.NewTime(tableName, "expire") - _resourcePss.LimitDay = field.NewInt32(tableName, "limit_day") - _resourcePss.LastUsed = field.NewTime(tableName, "last_used") + _resourcePss.DailyLimit = field.NewInt32(tableName, "daily_limit") + _resourcePss.DailyUsed = field.NewInt32(tableName, "daily_used") + _resourcePss.DailyLast = field.NewTime(tableName, "daily_last") _resourcePss.CreatedAt = field.NewTime(tableName, "created_at") _resourcePss.UpdatedAt = field.NewTime(tableName, "updated_at") _resourcePss.DeletedAt = field.NewField(tableName, "deleted_at") @@ -52,14 +52,14 @@ type resourcePss struct { ALL field.Asterisk ID field.Int32 // ID ResourceID field.Int32 // 套餐ID - Active field.Bool // 是否启用 Type field.Int32 // 套餐类型:1-包时,2-包量 Live field.Int32 // 可用时长(秒) Quota field.Int32 // 配额数量 Used field.Int32 // 已用数量 Expire field.Time // 过期时间 - LimitDay field.Int32 // 每日限额 - LastUsed field.Time // 最后提取时间 + DailyLimit field.Int32 // 每日限制 + DailyUsed field.Int32 // 今日已用数量 + DailyLast field.Time // 今日最后使用时间 CreatedAt field.Time // 创建时间 UpdatedAt field.Time // 更新时间 DeletedAt field.Field // 删除时间 @@ -81,14 +81,14 @@ func (r *resourcePss) updateTableName(table string) *resourcePss { r.ALL = field.NewAsterisk(table) r.ID = field.NewInt32(table, "id") r.ResourceID = field.NewInt32(table, "resource_id") - r.Active = field.NewBool(table, "active") r.Type = field.NewInt32(table, "type") r.Live = field.NewInt32(table, "live") r.Quota = field.NewInt32(table, "quota") r.Used = field.NewInt32(table, "used") r.Expire = field.NewTime(table, "expire") - r.LimitDay = field.NewInt32(table, "limit_day") - r.LastUsed = field.NewTime(table, "last_used") + r.DailyLimit = field.NewInt32(table, "daily_limit") + r.DailyUsed = field.NewInt32(table, "daily_used") + r.DailyLast = field.NewTime(table, "daily_last") r.CreatedAt = field.NewTime(table, "created_at") r.UpdatedAt = field.NewTime(table, "updated_at") r.DeletedAt = field.NewField(table, "deleted_at") @@ -111,14 +111,14 @@ func (r *resourcePss) fillFieldMap() { r.fieldMap = make(map[string]field.Expr, 13) r.fieldMap["id"] = r.ID r.fieldMap["resource_id"] = r.ResourceID - r.fieldMap["active"] = r.Active r.fieldMap["type"] = r.Type r.fieldMap["live"] = r.Live r.fieldMap["quota"] = r.Quota r.fieldMap["used"] = r.Used r.fieldMap["expire"] = r.Expire - r.fieldMap["limit_day"] = r.LimitDay - r.fieldMap["last_used"] = r.LastUsed + r.fieldMap["daily_limit"] = r.DailyLimit + r.fieldMap["daily_used"] = r.DailyUsed + r.fieldMap["daily_last"] = r.DailyLast r.fieldMap["created_at"] = r.CreatedAt r.fieldMap["updated_at"] = r.UpdatedAt r.fieldMap["deleted_at"] = r.DeletedAt diff --git a/web/router.go b/web/router.go index 9dcee64..6549253 100644 --- a/web/router.go +++ b/web/router.go @@ -9,13 +9,17 @@ import ( func ApplyRouters(app *fiber.App) { api := app.Group("/api") - // 认证路由 + // 认证 auth := api.Group("/auth") - auth.Post("/verify/sms", Protect(), handlers.SmsCode) - auth.Post("/login/sms", Protect(), handlers.Login) + auth.Post("/verify/sms", PermitUser(), handlers.SmsCode) + auth.Post("/login/sms", PermitUser(), handlers.Login) auth.Post("/token", handlers.Token) - // 客户端路由 + // 客户端 client := api.Group("/client") client.Get("/test/create", handlers.CreateClient) + + // 通道 + channel := api.Group("/channel", PermitUser()) + channel.Post("/create", handlers.CreateChannel) } diff --git a/web/services/channel.go b/web/services/channel.go index 8586ad3..9a9bb6e 100644 --- a/web/services/channel.go +++ b/web/services/channel.go @@ -1,10 +1,20 @@ package services import ( + "context" "errors" - "log/slog" + "fmt" + "math" + "platform/pkg/rds" + "platform/web/common" "platform/web/models" q "platform/web/queries" + "time" + + "github.com/google/uuid" + "github.com/jxskiss/base62" + "github.com/redis/go-redis/v9" + "gorm.io/gorm" ) var Channel = &channelService{} @@ -12,47 +22,229 @@ var Channel = &channelService{} type channelService struct { } +// CreateChannel 创建连接通道,并返回连接信息,如果配额不足则返回错误 func (s *channelService) CreateChannel( - userID int32, - region string, - provider string, - protocol Protocol, - resourceId int, + ctx context.Context, + auth *AuthContext, + resourceId int32, + protocol ChannelProtocol, + authType ChannelAuthType, count int, + nodeFilter ...NodeFilterConfig, ) ([]*models.Channel, error) { - // 检查并扣减套餐余额 - var resourceInfo = struct { - models.Resource - models.ResourcePss - }{} - err := q.Resource. - Where(q.Resource.UserID.Eq(userID)). - LeftJoin(q.ResourcePss, q.ResourcePss.ResourceID.EqCol(q.Resource.ID)). - Scan(&resourceInfo) + // 创建通道 + var channels []*models.Channel + err := q.Q.Transaction(func(tx *q.Query) error { + // 查找套餐 + var resource = struct { + data models.Resource + pss models.ResourcePss + }{} + err := q.Resource.As("data"). + LeftJoin(q.ResourcePss.As("pss"), q.ResourcePss.ResourceID.EqCol(q.Resource.ID)). + Where(q.Resource.ID.Eq(resourceId)). + Scan(&resource) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return ChannelServiceErr("套餐不存在") + } + return err + } + + // 检查使用人 + if auth.Payload.Type == PayloadUser && auth.Payload.Id != resource.data.UserID { + return common.AuthForbiddenErr("无权限访问") + } + + // 检查套餐状态 + if !resource.data.Active { + return ChannelServiceErr("套餐已失效") + } + + // 检查每日限额 + today := time.Now().Format("2006-01-02") == resource.pss.DailyLast.Format("2006-01-02") + dailyRemain := int(math.Max(float64(resource.pss.DailyLimit-resource.pss.DailyUsed), 0)) + if today && dailyRemain < count { + return ChannelServiceErr("套餐每日配额不足") + } + + // 检查时间或配额 + if resource.pss.Type == 1 { // 包时 + if resource.pss.Expire.Before(time.Now()) { + return ChannelServiceErr("套餐已过期") + } + } else { // 包量 + remain := int(math.Max(float64(resource.pss.Quota-resource.pss.Used), 0)) + if remain < count { + return ChannelServiceErr("套餐配额不足") + } + } + + // 筛选可用节点 + nodes, err := Node.Filter(auth.Payload.Id, protocol, count, nodeFilter...) + if err != nil { + return err + } + + // 获取用户配置白名单 + whitelist, err := q.Whitelist.Where( + q.Whitelist.UserID.Eq(auth.Payload.Id), + ).Find() + if err != nil { + return err + } + + // 创建连接通道 + channels = make([]*models.Channel, 0, len(nodes)*len(whitelist)) + for _, node := range nodes { + for _, allowed := range whitelist { + username, password := genPassPair() + channels = append(channels, &models.Channel{ + UserID: auth.Payload.Id, + NodeID: node.ID, + NodePort: node.FwdPort, + Protocol: string(protocol), + AuthIP: authType == ChannelAuthTypeIp, + UserHost: allowed.Host, + AuthPass: authType == ChannelAuthTypePass, + Username: username, + Password: password, + Expiration: time.Now().Add(time.Duration(resource.pss.Live) * time.Second), + }) + } + } + + // 保存到数据库 + err = tx.Channel.Create(channels...) + if err != nil { + return err + } + + // 更新套餐使用记录 + if today { + resource.pss.DailyUsed += int32(count) + resource.pss.Used += int32(count) + } else { + resource.pss.DailyLast = time.Now() + resource.pss.DailyUsed = int32(count) + resource.pss.Used += int32(count) + } + + err = tx.ResourcePss. + Where(q.ResourcePss.ID.Eq(resource.pss.ID)). + Omit(q.ResourcePss.ResourceID). + Save(&resource.pss) + if err != nil { + return err + } + + return nil + }) if err != nil { return nil, err } - slog.Debug("查询资源", slog.Any("info", resourceInfo)) - - // 创建连接通道 - - // 保存到数据库与缓存,以及计时关闭 - - // 组织请求数据 - - // 发送请求到远端配置服务 + // 缓存通道信息与异步删除任务 + pipe := rds.Client.TxPipeline() + zList := make([]redis.Z, 0, len(channels)) + for _, channel := range channels { + pipe.Set(ctx, chKey(channel), channel, channel.Expiration.Sub(time.Now())) + zList = append(zList, redis.Z{ + Score: float64(channel.Expiration.Unix()), + Member: channel.ID, + }) + } + pipe.ZAdd(ctx, "tasks:channel", zList...) + _, err = pipe.Exec(ctx) + if err != nil { + return nil, err + } // 返回连接通道列表 - - return nil, errors.New("not implemented") + return channels, errors.New("not implemented") } -type Protocol string +type ChannelAuthType int const ( - ProtocolSocks5 = Protocol("socks5") - ProtocolHTTP = Protocol("http") - ProtocolHttps = Protocol("https") + ChannelAuthTypeIp = iota + ChannelAuthTypePass ) + +type ChannelProtocol string + +const ( + ProtocolSocks5 = ChannelProtocol("socks5") + ProtocolHTTP = ChannelProtocol("http") + ProtocolHttps = ChannelProtocol("https") +) + +func genPassPair() (string, string) { + usernameBytes, err := uuid.New().MarshalBinary() + if err != nil { + panic(err) + } + passwordBytes, err := uuid.New().MarshalBinary() + if err != nil { + panic(err) + } + username := base62.EncodeToString(usernameBytes) + password := base62.EncodeToString(passwordBytes) + return username, password +} + +func (s *channelService) RemoveChannels(auth *AuthContext, id ...int32) error { + + var channels []*models.Channel + + // 删除通道 + err := q.Q.Transaction(func(tx *q.Query) error { + // 查找通道 + channels, err := tx.Channel.Where( + q.Channel.ID.In(id...), + ).Find() + if err != nil { + return err + } + + // 检查权限,只有用户自己和管理员能删除 + for _, channel := range channels { + if auth.Payload.Type == PayloadUser && auth.Payload.Id != channel.UserID { + return common.AuthForbiddenErr("无权限访问") + } + } + + // 删除指定的通道 + result, err := tx.Channel.Delete(channels...) + if err != nil { + return err + } + if result.RowsAffected != int64(len(channels)) { + return ChannelServiceErr("删除通道失败") + } + + return nil + }) + if err != nil { + return err + } + + // 删除缓存,异步任务直接在消费端处理删除 + pipe := rds.Client.TxPipeline() + for _, channel := range channels { + pipe.Del(context.Background(), chKey(channel)) + } + + return nil +} + +func chKey(channel *models.Channel) string { + return fmt.Sprintf("channel:%s:%d", channel.UserHost, channel.NodePort) +} + +type ChannelServiceErr string + +func (c ChannelServiceErr) Error() string { + return string(c) +} diff --git a/web/services/channel_test.go b/web/services/channel_test.go deleted file mode 100644 index 33064f3..0000000 --- a/web/services/channel_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package services - -import ( - "testing" -) - -func Test_channelService_CreateChannel(t *testing.T) { - // type args struct { - // userID int32 - // region string - // provider string - // protocol Protocol - // resourceId int - // count int - // } - // tests := []struct { - // name string - // args args - // want []*models.Channel - // wantErr bool - // }{ - // // TODO: Add test cases. - // } - // for _, tt := range tests { - // t.Run(tt.name, func(t *testing.T) { - // s := &channelService{} - // got, err := s.CreateChannel(tt.args.userID, tt.args.region, tt.args.provider, tt.args.protocol, tt.args.resourceId, tt.args.count) - // if (err != nil) != tt.wantErr { - // t.Errorf("CreateChannel() error = %v, wantErr %v", err, tt.wantErr) - // return - // } - // if !reflect.DeepEqual(got, tt.want) { - // t.Errorf("CreateChannel() got = %v, want %v", got, tt.want) - // } - // }) - // } -} diff --git a/web/services/node.go b/web/services/node.go new file mode 100644 index 0000000..ca2ddb8 --- /dev/null +++ b/web/services/node.go @@ -0,0 +1,18 @@ +package services + +import "platform/web/models" + +var Node = &nodeService{} + +type nodeService struct{} + +func (s *nodeService) Filter(userId int32, proto ChannelProtocol, count int, config ...NodeFilterConfig) ([]*models.Node, error) { + + return make([]*models.Node, 0), nil +} + +type NodeFilterConfig struct { + province string + city string + provider string +} diff --git a/web/services/session.go b/web/services/session.go index b93dccf..222288d 100644 --- a/web/services/session.go +++ b/web/services/session.go @@ -5,7 +5,7 @@ import ( "encoding/json" "errors" "fmt" - "platform/init/rds" + "platform/pkg/rds" "time" "github.com/google/uuid" diff --git a/web/services/session_test.go b/web/services/session_test.go index 25645a2..d5f0cb3 100644 --- a/web/services/session_test.go +++ b/web/services/session_test.go @@ -3,7 +3,7 @@ package services import ( "context" "errors" - "platform/init/rds" + "platform/pkg/rds" "reflect" "testing" "time" diff --git a/web/services/verifier.go b/web/services/verifier.go index eb13398..f070064 100644 --- a/web/services/verifier.go +++ b/web/services/verifier.go @@ -6,7 +6,7 @@ import ( "fmt" "log/slog" "math/rand" - "platform/init/rds" + "platform/pkg/rds" "strconv" "time" diff --git a/web/services/verifier_test.go b/web/services/verifier_test.go index 2a14b61..cf7c064 100644 --- a/web/services/verifier_test.go +++ b/web/services/verifier_test.go @@ -2,7 +2,7 @@ package services import ( "context" - "platform/init/rds" + "platform/pkg/rds" "strconv" "testing" "time" diff --git a/web/web.go b/web/web.go index 410ba9b..a510427 100644 --- a/web/web.go +++ b/web/web.go @@ -1,7 +1,7 @@ package web import ( - "platform/init/env" + "platform/pkg/env" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger"