ocdp v1
This commit is contained in:
324
backend/CODE_FIRST_GUIDE.md
Normal file
324
backend/CODE_FIRST_GUIDE.md
Normal file
@ -0,0 +1,324 @@
|
||||
# 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 文档始终与代码保持同步!✨
|
||||
Reference in New Issue
Block a user