# OCDP 命名约定对照表

## 快速参考

| 层级 | 变量/属性 | 类型名 | JSON 字段 |
|-----|----------|-------|----------|
| **Backend Go** | 导出: `PascalCase`<br>不导出: `camelCase` | `PascalCase` | `snake_case` |
| **OpenAPI Schema** | `snake_case` | `PascalCase` | `snake_case` |
| **Frontend Generated** | `snake_case` (引号) | `PascalCase` | `snake_case` |
| **Frontend Internal** | `camelCase` | `PascalCase` | `snake_case` |

---

## 详细说明

### 1. Backend (Go)

```go
// 文件: backend/internal/adapter/input/http/dto/cluster_dto.go

type ClusterResponse struct {
    ID        string `json:"id"`            // 导出字段: PascalCase, JSON: snake_case
    Name      string `json:"name"`
    HasCAData bool   `json:"has_ca_data"`   // Go: PascalCase → JSON: snake_case
    CreatedAt string `json:"created_at"`
}

// 内部变量
func example() {
    var clusterId string      // 不导出: camelCase
    var clusterName string
}
```

**规则**:
- ✅ 导出变量/字段: `PascalCase` (首字母大写)
- ✅ 不导出变量/字段: `camelCase` (首字母小写)
- ✅ 类型名: `PascalCase`
- ✅ JSON 标签: `snake_case`

---

### 2. OpenAPI 规范 (openapi.yaml)

```yaml
# 文件: backend/docs/openapi.yaml

components:
  schemas:
    ClusterResponse:              # Schema 名称: PascalCase
      type: object
      properties:
        id:                        # 属性: snake_case
          type: string
        name:
          type: string
        has_ca_data:              # 属性: snake_case (与 Go JSON 标签一致)
          type: boolean
        created_at:               # 属性: snake_case
          type: string
```

**规则**:
- ✅ 固定字段 (operationId, paths, etc.): `camelCase`
- ✅ Schema 本身: `PascalCase`
- ✅ Schema 下面的属性: `snake_case`

---

### 3. Frontend TypeScript - 生成的 API Client

```typescript
// 文件: frontend/src/api/generated/models/cluster-response.ts
// 自动生成，不要手动修改

export interface ClusterResponse {
    'id'?: string;                // 属性: snake_case (加引号)
    'name'?: string;
    'has_ca_data'?: boolean;      // 保持 snake_case，与 JSON 一致
    'created_at'?: string;
}
```

**规则**:
- ✅ 类型名: `PascalCase`
- ✅ 属性: `snake_case` (带引号)
- ⚠️ 不要手动修改生成的文件

---

### 4. Frontend TypeScript - 内部类型

```typescript
// 文件: frontend/src/core/types/index.ts
// 前端内部使用的类型定义

export interface Cluster {
  id: string;                    // 内部变量: camelCase
  name: string;
  hasCAData?: boolean;          // camelCase (前端惯例)
  hasCertData?: boolean;
  createdAt: string;            // camelCase
  updatedAt: string;
}

// API 请求类型 (保持与后端一致)
export interface CreateClusterRequest {
  name: string;
  host: string;
  ca_data: string;              // JSON 字段: snake_case
  cert_data: string;            // 与后端 API 保持一致
  key_data: string;
}
```

**规则**:
- ✅ 内部变量: `camelCase`
- ✅ 类型名: `PascalCase`
- ✅ JSON 序列化 (API 通信): `snake_case`

---

## 数据流转示例

### 完整的请求-响应流程

```
┌─────────────────────────────────────────────────────────────┐
│ 1. 前端组件 (camelCase)                                       │
├─────────────────────────────────────────────────────────────┤
│ const cluster = {                                            │
│   name: "Production",                                        │
│   hasCAData: true,        // camelCase                       │
│   createdAt: "2025-11-10"                                    │
│ }                                                            │
└─────────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────────┐
│ 2. API Request Body (snake_case JSON)                       │
├─────────────────────────────────────────────────────────────┤
│ {                                                            │
│   "name": "Production",                                      │
│   "ca_data": "LS0t...",    // snake_case                     │
│   "cert_data": "LS0t..."                                     │
│ }                                                            │
└─────────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────────┐
│ 3. Backend Go 结构体 (PascalCase)                             │
├─────────────────────────────────────────────────────────────┤
│ type CreateClusterRequest struct {                           │
│   Name     string `json:"name"`                              │
│   CAData   string `json:"ca_data"`   // Go: PascalCase       │
│   CertData string `json:"cert_data"` // JSON: snake_case     │
│ }                                                            │
└─────────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────────┐
│ 4. API Response JSON (snake_case)                           │
├─────────────────────────────────────────────────────────────┤
│ {                                                            │
│   "id": "cluster-123",                                       │
│   "name": "Production",                                      │
│   "has_ca_data": true,     // snake_case                     │
│   "created_at": "2025-11-10T08:00:00Z"                       │
│ }                                                            │
└─────────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────────┐
│ 5. 前端接收 (可以保持 snake_case 或转换为 camelCase)             │
├─────────────────────────────────────────────────────────────┤
│ // 选项 A: 直接使用生成的类型 (snake_case)                      │
│ const cluster: ClusterResponse = response;                   │
│ console.log(cluster.has_ca_data);                            │
│                                                              │
│ // 选项 B: 转换为内部类型 (camelCase)                           │
│ const cluster: Cluster = {                                   │
│   id: response.id,                                           │
│   hasCAData: response.has_ca_data,  // 转换                   │
│   createdAt: response.created_at    // 转换                   │
│ };                                                           │
└─────────────────────────────────────────────────────────────┘
```

---

## 命名转换对照

### 常见字段名转换

| Go (PascalCase) | JSON (snake_case) | TS Generated | TS Internal (camelCase) |
|----------------|------------------|--------------|------------------------|
| `ID` | `id` | `'id'` | `id` |
| `Name` | `name` | `'name'` | `name` |
| `ClusterID` | `cluster_id` | `'cluster_id'` | `clusterId` |
| `RegistryID` | `registry_id` | `'registry_id'` | `registryId` |
| `HasCAData` | `has_ca_data` | `'has_ca_data'` | `hasCAData` |
| `CAData` | `ca_data` | `'ca_data'` | `caData` |
| `CertData` | `cert_data` | `'cert_data'` | `certData` |
| `KeyData` | `key_data` | `'key_data'` | `keyData` |
| `CreatedAt` | `created_at` | `'created_at'` | `createdAt` |
| `UpdatedAt` | `updated_at` | `'updated_at'` | `updatedAt` |

---

## 重新生成 OpenAPI Client

### 安装依赖

```bash
# 安装 Java (如果尚未安装)
sudo apt-get install openjdk-11-jdk

# 安装 OpenAPI Generator CLI (全局)
npm install -g @openapitools/openapi-generator-cli
```

### 生成命令

```bash
# 方式 1: 使用项目根目录的 Makefile (推荐)
cd /home/mango/workspace/ocdp-go
make openapi-gen-frontend

# 方式 2: 使用前端目录的 npm 脚本
cd /home/mango/workspace/ocdp-go/frontend
npm run openapi-gen

# 方式 3: 直接运行 (如果需要自定义参数)
cd /home/mango/workspace/ocdp-go
openapi-generator-cli generate \
  -i backend/docs/openapi.yaml \
  -g typescript-axios \
  -o frontend/src/api/generated \
  --additional-properties=supportsES6=true,withSeparateModelsAndApi=true,apiPackage=api,modelPackage=models
```

### 文件权限问题解决

如果遇到权限问题 (文件属于 root):

```bash
# 修改生成文件的所有权
sudo chown -R $USER:$USER frontend/src/api/generated

# 然后重新生成
make openapi-gen-frontend
```

---

## 最佳实践

### ✅ 推荐做法

1. **使用生成的类型进行 API 通信**
   ```typescript
   import type { ClusterResponse } from "@/api/generated";
   const clusters = await apiRequest<ClusterResponse[]>("/v1/clusters");
   ```

2. **统一使用 apiRequest helper**
   ```typescript
   import { apiRequest } from "@/shared/utils/api-helpers";
   // 自动处理认证、错误、token 刷新
   ```

3. **后端修改 OpenAPI 后，重新生成前端 client**
   ```bash
   make openapi-gen-frontend
   ```

### ❌ 避免的做法

1. **不要手动修改生成的代码**
   ```typescript
   // ❌ 不要修改 /frontend/src/api/generated/ 下的文件
   // 这些文件会在重新生成时被覆盖
   ```

2. **不要直接使用 fetch**
   ```typescript
   // ❌ 不推荐
   const response = await fetch("/api/v1/clusters");
   
   // ✅ 推荐
   const clusters = await apiRequest("/v1/clusters");
   ```

3. **避免混淆命名约定**
   ```typescript
   // ❌ 不要在 API 请求中使用 camelCase
   const request = {
     name: "Test",
     caData: "xxx",      // 错误! 应该是 ca_data
   };
   
   // ✅ 正确
   const request = {
     name: "Test",
     ca_data: "xxx",     // 与后端 JSON 标签一致
   };
   ```

---

## 快速检查清单

### Backend (Go)

- [ ] 导出字段使用 `PascalCase`
- [ ] JSON 标签使用 `snake_case`
- [ ] 更新 OpenAPI 规范与代码保持一致

### OpenAPI 规范

- [ ] Schema 名称使用 `PascalCase`
- [ ] 属性使用 `snake_case`
- [ ] 与后端 DTO 的 JSON 标签一致

### Frontend

- [ ] 从 OpenAPI 重新生成 client
- [ ] 使用生成的类型进行 API 通信
- [ ] 内部类型可以使用 `camelCase` (可选)
- [ ] 使用 `apiRequest` helper

---

## 相关文档

- [API Client 详细说明](./frontend/API_CLIENT_CONVENTIONS.md)
- [OpenAPI 规范](./backend/docs/openapi.yaml)
- [前端 API Helper](./frontend/src/shared/utils/api-helpers.ts)

---

**更新日期**: 2025-11-10

