ocdp v1
This commit is contained in:
339
docs/development/naming-conventions.md
Normal file
339
docs/development/naming-conventions.md
Normal file
@ -0,0 +1,339 @@
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user