# 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