Files
ocdp-go/backend/CODE_FIRST_GUIDE.md
mangomqy c5e51ed069 ocdp v1
2025-11-13 02:54:06 +00:00

325 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Code First API 开发指南
## 🎯 概述
本项目现已支持 **Code First** 方式开发 API
1. **后端**:使用 Swagger 注释从代码生成 OpenAPI 文档
2. **前端**:使用 Orval 从 OpenAPI 文档生成 TypeScript 客户端
---
## 🛠️ 工具链
### 后端
- **swaggo/swag**: 从 Go 代码注释生成 OpenAPI/Swagger 文档
- 文档https://github.com/swaggo/swag
### 前端
- **Orval**: 从 OpenAPI 规范生成 TypeScript API 客户端
- 文档https://orval.dev/
---
## 📝 开发流程
### 1. 后端:添加 Swagger 注释
#### 主程序注释 (cmd/api/main.go)
```go
// @title OCDP Backend API
// @version 1.0
// @description OCDP (Open Cloud Development Platform) Backend API
//
// @host localhost:8080
// @BasePath /api/v1
//
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
package main
```
#### Handler 方法注释示例
```go
// ListArtifacts 列出 repository 中的所有 artifacts
// @Summary 列出 Repository 中的所有 Artifacts
// @Description 列出指定 Repository 中的所有 Artifact支持按类型过滤
// @Tags Artifacts
// @Accept json
// @Produce json
// @Param registry_id path string true "Registry ID"
// @Param repository_name path string true "Repository Name (URL encoded)"
// @Param media_type query string false "过滤类型 (all, chart, image, other)" default(all)
// @Success 200 {array} dto.TagResponse
// @Failure 500 {object} dto.ErrorResponse
// @Router /registries/{registry_id}/repositories/{repository_name}/artifacts [get]
func (h *ArtifactHandler) ListArtifacts(w http.ResponseWriter, r *http.Request) {
// ...
}
```
### 2. 生成 OpenAPI 文档
```bash
# 在 backend 目录下运行
cd backend
swag init -g cmd/api/main.go -o docs/swagger --parseDependency --parseInternal
# 生成的文件在 docs/swagger/ 目录:
# - swagger.json
# - swagger.yaml
# - docs.go
```
### 3. 前端:重新生成 API 客户端
```bash
# 在 frontend 目录下运行
cd frontend
npm run openapi-gen
# 或者
orval
```
生成的文件在 `src/api/generated-orval/` 目录。
---
## 🔄 完整工作流
### 添加新 API 的步骤
#### 1. 后端开发
```bash
# 1.1 创建 DTO
# internal/adapter/input/http/dto/new_feature_dto.go
# 1.2 创建 Handler 并添加 Swagger 注释
# internal/adapter/input/http/rest/new_feature_handler.go
# 1.3 注册路由
# cmd/api/main.go
# 1.4 生成 OpenAPI 文档
cd backend
swag init -g cmd/api/main.go -o docs/swagger --parseDependency --parseInternal
# 1.5 复制到主文档 (可选)
cp docs/swagger/swagger.yaml docs/openapi.yaml
```
#### 2. 前端开发
```bash
# 2.1 重新生成 API 客户端
cd frontend
npm run openapi-gen
# 2.2 使用生成的客户端
import { listArtifacts } from '@/api/generated-orval/api';
const data = await listArtifacts({
registryId: 'xxx',
repositoryName: 'charts/nginx'
});
```
---
## 📋 Swagger 注释速查
### 常用标签
| 标签 | 说明 | 示例 |
|------|------|------|
| `@Summary` | 简短描述 | `@Summary 列出所有用户` |
| `@Description` | 详细描述 | `@Description 返回系统中的所有用户列表` |
| `@Tags` | API 分组 | `@Tags Users` |
| `@Accept` | 接受的格式 | `@Accept json` |
| `@Produce` | 返回的格式 | `@Produce json` |
| `@Param` | 参数 | `@Param id path string true "User ID"` |
| `@Success` | 成功响应 | `@Success 200 {object} dto.UserResponse` |
| `@Failure` | 失败响应 | `@Failure 404 {object} dto.ErrorResponse` |
| `@Router` | 路由定义 | `@Router /users/{id} [get]` |
| `@Security` | 安全认证 | `@Security BearerAuth` |
### 参数类型
```go
// 路径参数 (path)
@Param id path string true "User ID"
// 查询参数 (query)
@Param page query int false "Page number" default(1)
@Param size query int false "Page size" default(20)
// 请求体 (body)
@Param request body dto.CreateUserRequest true "User data"
// Header 参数 (header)
@Param X-Request-ID header string false "Request ID"
```
### 响应定义
```go
// 单个对象
@Success 200 {object} dto.UserResponse
// 数组
@Success 200 {array} dto.UserResponse
// 自定义响应
@Success 200 {object} map[string]interface{}
// 多个可能的响应
@Success 200 {object} dto.UserResponse "Success"
@Success 201 {object} dto.UserResponse "Created"
@Failure 400 {object} dto.ErrorResponse "Bad Request"
@Failure 404 {object} dto.ErrorResponse "Not Found"
@Failure 500 {object} dto.ErrorResponse "Internal Error"
```
---
## 🎯 最佳实践
### 1. 命名规范
| 层级 | 风格 | 示例 |
|------|------|------|
| **路径变量** | snake_case | `{registry_id}`, `{repository_name}` |
| **查询参数** | snake_case | `?media_type=chart` |
| **JSON 字段** | camelCase | `repositoryName`, `mediaType` |
| **Go 结构体** | PascalCase | `RepositoryName`, `MediaType` |
### 2. DTO 定义示例
```go
// ArtifactResponse Artifact 响应
type ArtifactResponse struct {
RepositoryName string `json:"repositoryName"` // Go: PascalCase, JSON: camelCase
Tag string `json:"tag"`
Type string `json:"type"`
Size int64 `json:"size"`
CreatedAt string `json:"createdAt"`
}
```
### 3. 完整的 Handler 示例
```go
// GetArtifact 获取 artifact 详情
// @Summary 获取 Artifact 详情
// @Description 获取指定 Artifact 的详细信息
// @Tags Artifacts
// @Accept json
// @Produce json
// @Param registry_id path string true "Registry ID"
// @Param repository_name path string true "Repository Name (URL encoded)"
// @Param reference path string true "Artifact Reference (tag or digest)"
// @Success 200 {object} dto.ArtifactResponse
// @Failure 404 {object} dto.ErrorResponse "Artifact not found"
// @Failure 500 {object} dto.ErrorResponse "Internal server error"
// @Router /registries/{registry_id}/repositories/{repository_name}/artifacts/{reference} [get]
func (h *ArtifactHandler) GetArtifact(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
registryID := vars["registry_id"]
repositoryName := vars["repository_name"]
reference := vars["reference"]
artifact, err := h.artifactService.GetArtifact(r.Context(), registryID, repositoryName, reference)
if err != nil {
respondError(w, http.StatusNotFound, "Artifact not found", err.Error())
return
}
response := &dto.ArtifactResponse{
RepositoryName: artifact.Repository,
Tag: artifact.Tag,
Digest: artifact.Digest,
Type: string(artifact.Type),
Size: artifact.Size,
CreatedAt: artifact.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
}
respondJSON(w, http.StatusOK, response)
}
```
---
## 🔧 故障排除
### 问题 1: swag 命令未找到
```bash
# 解决方案:安装 swag
go install github.com/swaggo/swag/cmd/swag@latest
# 确保 $GOPATH/bin 在 PATH 中
export PATH=$PATH:$(go env GOPATH)/bin
```
### 问题 2: 生成的文档不完整
```bash
# 确保使用了正确的参数
swag init -g cmd/api/main.go -o docs/swagger --parseDependency --parseInternal
# 检查注释格式是否正确
# 注释必须紧邻函数定义,中间不能有空行
```
### 问题 3: 前端客户端生成失败
```bash
# 检查 OpenAPI 文档是否有效
cd frontend
npx @apidevtools/swagger-cli validate ../backend/docs/openapi.yaml
# 清理并重新生成
rm -rf src/api/generated-orval
npm run openapi-gen
```
---
## 📚 参考资源
- **Swaggo 文档**: https://github.com/swaggo/swag
- **Swaggo 声明式注释**: https://github.com/swaggo/swag#declarative-comments-format
- **Orval 文档**: https://orval.dev/
- **OpenAPI 规范**: https://swagger.io/specification/
---
## ✅ 当前状态
### 已完成
- [x] 安装 swag 工具
- [x] 添加主程序 Swagger 注释
- [x] 为 Artifact Handler 添加完整注释
- [x] 生成 OpenAPI 文档
- [x] 前端配置 Orval
- [x] 重新生成前端客户端
### 待完成 (可选)
- [ ] 为其他 Handler 添加 Swagger 注释
- [ ] Auth Handler
- [ ] Cluster Handler
- [ ] Registry Handler
- [ ] Instance Handler
- [ ] Monitoring Handler
- [ ] 添加更详细的错误响应定义
- [ ] 添加请求/响应示例
- [ ] 配置 CI/CD 自动生成文档
---
## 🎉 总结
现在项目支持 Code First 开发流程:
1. **后端开发者**:写代码 + 注释 → 生成 OpenAPI
2. **前端开发者**:使用 OpenAPI → 生成 TypeScript 客户端
3. **文档自动同步**:代码即文档,保证一致性
这确保了 API 文档始终与代码保持同步!✨