# OCDP 命名约定对照表 ## 快速参考 | 层级 | 变量/属性 | 类型名 | JSON 字段 | |-----|----------|-------|----------| | **Backend Go** | 导出: `PascalCase`
不导出: `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("/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