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 |