Based on my thorough exploration, here's a comprehensive analysis of the node API and data access layer: --- ## 节点API和数据访问层完整分析 ### 1. 节点CRUD相关Repository方法 #### 核心方法 (`go-backend/internal/store/repo/`) | 方法 | 文件位置 | 功能 | |------|----------|------| | `CreateNode()` | `repository_mutations.go:199` | 创建节点,处理 serverIP/serverIPV4/serverIPV6 | | `UpdateNode()` | `repository_mutations.go:241` | 更新节点,包含所有IP字段 | | `DeleteNodeCascade()` | `repository_mutations.go:299` | 级联删除节点(关联 ForwardPort、ChainTunnel、FederationTunnelBinding) | | `GetNodeByID()` | `repository.go:555` | 通过ID获取节点 | | `GetNodeBySecret()` | `repository.go:540` | 通过Secret获取节点 | | `GetNodeStatusFields()` | `repository_mutations.go:229` | 获取节点状态字段 | | `GetNodeSecret()` | `repository_mutations.go:263` | 获取节点密钥 | | `GetNodeRemoteFields()` | `repository_mutations.go:317` | 获取远程节点字段 | | `GetNodePortRange()` | `repository_mutations.go:336` | 获取节点端口范围 | | `UpdateNodeOrder()` | `repository_mutations.go:287` | 更新节点排序 | | `UpdateNodeOnline()` | `repository.go:570` | 更新节点在线状态 | | `UpdateNodeStatus()` | `repository.go:580` | 更新节点状态 | | `ListNodes()` | `repository.go:625` | 列出所有节点(返回JSON map格式) | | `GetNodeRecord()` | `repository_control.go:131` | 获取 NodeRecord 视图对象 | | `CreateRemoteNode()` | `repository_federation.go:266` | 创建远程节点(联邦场景) | | `exportNodes()` | `repository.go:1660` | 导出节点数据(用于备份) | | `importNodes()` | `repository.go:2016` | 导入节点数据(用于恢复) | --- ### 2. 节点数据结构定义 #### GORM模型 (`model.Node` - 数据库存储) **文件:** `/go-backend/internal/store/model/model.go:59-82` ```go type Node struct { ID int64 `gorm:"primaryKey;autoIncrement"` Name string `gorm:"type:varchar(100);not null"` Secret string `gorm:"type:varchar(100);not null"` ServerIP string `gorm:"column:server_ip;type:varchar(100);not null"` ServerIPV4 sql.NullString `gorm:"column:server_ip_v4;type:varchar(100)"` // 注意:V大写 ServerIPV6 sql.NullString `gorm:"column:server_ip_v6;type:varchar(100)"` // ... 其他字段 } ``` #### 控制平面视图 (`model.NodeRecord`) **文件:** `/go-backend/internal/store/model/model.go:515-531` ```go type NodeRecord struct { ID int64 Name string ServerIP string ServerIPv4 string // 注意:v小写 ServerIPv6 string Status int PortRange string // ... 其他字段 } ``` #### JSON导出格式 (`model.NodeBackup`) **文件:** `/go-backend/internal/store/model/model.go:333-356` ```go type NodeBackup struct { ID int64 `json:"id"` Name string `json:"name"` ServerIP string `json:"serverIp"` ServerIPv4 string `json:"serverIpV4,omitempty"` ServerIPv6 string `json:"serverIpV6,omitempty"` // ... 其他字段 } ``` #### Handler层别名 **文件:** `/go-backend/internal/http/handler/control_plane.go:25` ```go type nodeRecord = model.NodeRecord // 类型别名 ``` --- ### 3. 节点数据返回给前端的格式 #### ListNodes API (`repository.go:625-652`) ```go // 返回 map[string]interface{} 格式 items = append(items, map[string]interface{}{ "id": n.ID, "inx": n.Inx, "name": n.Name, "ip": n.ServerIP, "serverIp": n.ServerIP, // ip和serverIp都返回 "serverIpV4": nullableString(n.ServerIPV4), "serverIpV6": nullableString(n.ServerIPV6), "port": n.Port, "tcpListenAddr": n.TCPListenAddr, "udpListenAddr": n.UDPListenAddr, "version": nullableString(n.Version), "http": n.HTTP, "tls": n.TLS, "socks": n.Socks, "status": n.Status, "isRemote": n.IsRemote, // ... }) ``` #### API端点 | 端点 | Handler | 功能 | |------|---------|------| | `POST /api/v1/node/list` | `h.nodeList` | 列出节点 | | `POST /api/v1/node/create` | `h.nodeCreate` | 创建节点 | | `POST /api/v1/node/update` | `h.nodeUpdate` | 更新节点 | | `POST /api/v1/node/delete` | `h.nodeDelete` | 删除节点 | --- ### 4. ServerIP/ServerIPV4/ServerIPV6 字段处理逻辑 #### 字段命名约定 | 层级 | 字段名 | 数据库列名 | |------|--------|-----------| | `model.Node` | `ServerIPV4`, `ServerIPV6` | `server_ip_v4`, `server_ip_v6` | | `model.NodeRecord` | `ServerIPv4`, `ServerIPv6` | - (内存结构) | | `model.NodeBackup` | `ServerIPv4`, `ServerIPv6` | - (JSON) | | 前端JSON | `serverIpV4`, `serverIpV6` | - | #### 模型转换函数 (`repository_control.go:161-201`) ```go func nodeRecordFromModel(n *model.Node) *model.NodeRecord { rec := &model.NodeRecord{ ID: n.ID, Name: n.Name, ServerIP: n.ServerIP, // ... } if n.ServerIPV4.Valid { rec.ServerIPv4 = strings.TrimSpace(n.ServerIPV4.String) } if n.ServerIPV6.Valid { rec.ServerIPv6 = strings.TrimSpace(n.ServerIPV6.String) } // ... } ``` #### Dual-Stack辅助函数 (`mutations.go:2715-2772`) ```go // 判断节点是否支持IPv4 func nodeSupportsV4(node *nodeRecord) bool { if strings.TrimSpace(node.ServerIPv4) != "" { return true } if strings.TrimSpace(node.ServerIPv6) != "" { return false } // 回退到 ServerIP 判断 legacy := strings.Trim(strings.TrimSpace(node.ServerIP), "[]") if ip := net.ParseIP(legacy); ip != nil { return ip.To4() != nil } return true } // 选择IPv4地址(优先显式字段,回退ServerIP) func pickNodeAddressV4(node *nodeRecord) string { if v := strings.TrimSpace(node.ServerIPv4); v != "" { return v } return strings.TrimSpace(node.ServerIP) } ``` --- ### 5. 扩展节点IP字段需要改动的位置 如果要添加新的IP字段(如 `ServerIPV4Alt`),需要修改以下位置: #### A. 模型层 (`go-backend/internal/store/model/model.go`) 1. **`model.Node`** - 添加 GORM 字段定义(约第64行) 2. **`model.NodeRecord`** - 添加视图字段(约第520行) 3. **`model.NodeBackup`** - 添加JSON字段(约第338行) #### B. Repository层 (`go-backend/internal/store/repo/`) 1. **`repository_mutations.go`** - `CreateNode()` 参数和赋值(约第199-227行) - `UpdateNode()` 参数和Updates map(约第241-261行) 2. **`repository_control.go`** - `nodeRecordFromModel()` 转换逻辑(约第161-201行) 3. **`repository_federation.go`** - `CreateRemoteNode()` 参数和赋值(约第266-294行) 4. **`repository.go`** - `ListNodes()` JSON返回格式(约第625-652行) - `exportNodes()` 导出逻辑(约第1660-1700行) - `importNodes()` 导入逻辑(约第2016-2057行) - `prepareSQLiteLegacyColumns()` 迁移逻辑(约第263行) #### C. Handler层 (`go-backend/internal/http/handler/`) 1. **`mutations.go`** - `nodeCreate()` 请求参数解析(约第234-279行) - `nodeUpdate()` 请求参数解析(约第281-336行) - `nodeSupportsV4/V6()` 函数逻辑(约第2715-2772行) - `pickNodeAddressV4/V6()` 函数逻辑 #### D. 测试层 (`go-backend/tests/contract/`) 所有 contract test 中的 `INSERT INTO node` 语句需要更新字段列表 --- ### 关键文件汇总 | 文件 | 用途 | |------|------| | `/go-backend/internal/store/model/model.go` | 数据模型定义 | | `/go-backend/internal/store/repo/repository_mutations.go` | CRUD操作实现 | | `/go-backend/internal/store/repo/repository_control.go` | 控制平面查询+模型转换 | | `/go-backend/internal/store/repo/repository_federation.go` | 联邦节点操作 | | `/go-backend/internal/store/repo/repository.go` | 核心查询+导出导入 | | `/go-backend/internal/http/handler/mutations.go` | HTTP API处理器 | | `/go-backend/internal/http/handler/control_plane.go` | 控制平面API |