Files
wenzi/docs/reports/architecture/OPENAPI_CONFIG.md

893 lines
23 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.
# 🦟 蚊子项目 - OpenAPI 3.0 文档配置
## 📋 概述
蚊子项目使用SpringDoc OpenAPI生成OpenAPI 3.0规范的API文档支持自动生成和实时更新。
## 🚀 快速开始
### 1. 添加依赖
```xml
<!-- pom.xml -->
<properties>
<springdoc.version>2.3.0</springdoc.version>
</properties>
<dependencies>
<!-- SpringDoc OpenAPI -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- Kubernetes支持可选 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-common</artifactId>
<version>${springdoc.version}</version>
</dependency>
</dependencies>
```
### 2. 基础配置
```yaml
# application-prod.yml
springdoc:
api-docs:
enabled: true
path: /api-docs
groups:
enabled: true
swagger-ui:
enabled: true
path: /swagger-ui.html
display-operation-id: true
display-request-duration: true
show-extensions: true
show-common-extensions: true
default-models-expand-depth: 2
default-model-expand-depth: 2
try-it-out-enabled: true
persist-authorization: true
tags-sorter: alpha
operations-sorter: alpha
group-configs:
- group: public
display-name: Public APIs
paths-to-match: /api/v1/**
- group: internal
display-name: Internal APIs
paths-to-match: /api/v1/internal/**
- group: admin
display-name: Admin APIs
paths-to-match: /api/v1/admin/**
```
### 3. OpenAPI配置类
```java
package com.mosquito.project.config;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import java.util.List;
/**
* OpenAPI配置类
*/
@Configuration
@Profile("prod")
public class OpenApiConfig {
@Value("${spring.application.name}")
private String applicationName;
@Value("${spring.application.version}")
private String applicationVersion;
@Value("${springdoc.api-docs.server.url}")
private String serverUrl;
@Bean
public OpenAPI mosquitoOpenAPI() {
// 安全方案定义
SecurityScheme apiKeyScheme = new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.name("X-API-Key")
.description("API密钥认证");
SecurityScheme bearerAuthScheme = new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("JWT Token认证");
// 安全要求
SecurityRequirement apiKeyRequirement = new SecurityRequirement()
.addList("API Key");
SecurityRequirement bearerAuthRequirement = new SecurityRequirement()
.addList("Bearer Auth");
// 服务器配置
Server server = new Server()
.url(serverUrl)
.description("生产环境服务器");
// 组件配置
Components components = new Components()
.addSecuritySchemes("API Key", apiKeyScheme)
.addSecuritySchemes("Bearer Auth", bearerAuthScheme);
return new OpenAPI()
.info(new Info()
.title("蚊子项目 API文档")
.description("蚊子项目推广活动管理系统的API接口文档")
.version(applicationVersion)
.contact(new Contact()
.name("蚊子项目团队")
.email("support@mosquito.com")
.url("https://mosquito.com"))
.license(new License()
.name("MIT License")
.url("https://opensource.org/licenses/MIT")))
.servers(List.of(server))
.components(components)
.addSecurityItem(apiKeyRequirement)
.addSecurityItem(bearerAuthRequirement);
}
}
```
### 4. 开发环境配置
```java
package com.mosquito.project.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.Collections;
import java.util.List;
/**
* Swagger配置开发环境
*/
@Configuration
@EnableOpenApi
@Profile("dev")
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.securitySchemes(Collections.singletonList(apiKey()))
.securityContexts(Collections.singletonList(securityContext()))
.select()
.apis(RequestHandlerSelectors.basePackage("com.mosquito.project.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("蚊子项目 API文档")
.description("蚊子项目推广活动管理系统的API接口文档")
.version("2.0.0")
.contact(new Contact(
"蚊子项目团队",
"https://mosquito.com",
"support@mosquito.com"))
.license("MIT License")
.licenseUrl("https://opensource.org/licenses/MIT")
.build();
}
private ApiKey apiKey() {
return new ApiKey("API Key", "X-API-Key", "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(operationContext -> true)
.build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
return Collections.singletonList(
new SecurityReference("API Key", new AuthorizationScope[]{authorizationScope})
);
}
}
```
## 📖 API注解示例
### 1. Controller注解
```java
package com.mosquito.project.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/activities")
@Tag(name = "活动管理", description = "活动相关的API接口")
@SecurityRequirement(name = "API Key")
public class ActivityController {
/**
* 创建活动
*/
@PostMapping
@Operation(
summary = "创建新活动",
description = "创建一个新的推广活动返回活动ID",
tags = {"活动管理"},
operationId = "createActivity"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "201",
description = "活动创建成功",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Activity.class)
)
),
@ApiResponse(
responseCode = "400",
description = "请求参数错误",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ApiResponse.class)
)
),
@ApiResponse(
responseCode = "401",
description = "API密钥无效",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ApiResponse.class)
)
)
})
public ResponseEntity<ApiResponse<Activity>> createActivity(
@Parameter(
name = "request",
description = "活动创建请求",
required = true,
schema = @Schema(implementation = CreateActivityRequest.class)
)
@RequestBody CreateActivityRequest request) {
// 实现逻辑
}
/**
* 获取活动详情
*/
@GetMapping("/{id}")
@Operation(
summary = "获取活动详情",
description = "根据活动ID获取活动的详细信息",
tags = {"活动管理"},
operationId = "getActivityById"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "活动详情",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Activity.class)
)
),
@ApiResponse(
responseCode = "404",
description = "活动不存在",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ApiResponse.class)
)
)
})
public ResponseEntity<ApiResponse<Activity>> getActivity(
@Parameter(
name = "id",
description = "活动ID",
required = true,
example = "1"
)
@PathVariable Long id) {
// 实现逻辑
}
/**
* 更新活动
*/
@PutMapping("/{id}")
@Operation(
summary = "更新活动信息",
description = "更新指定活动的信息",
tags = {"活动管理"},
operationId = "updateActivity"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "更新成功",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Activity.class)
)
),
@ApiResponse(
responseCode = "404",
description = "活动不存在"
)
})
public ResponseEntity<ApiResponse<Activity>> updateActivity(
@PathVariable Long id,
@RequestBody UpdateActivityRequest request) {
// 实现逻辑
}
/**
* 删除活动
*/
@DeleteMapping("/{id}")
@Operation(
summary = "删除活动",
description = "删除指定的活动",
tags = {"活动管理"},
operationId = "deleteActivity"
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "204",
description = "删除成功"
),
@ApiResponse(
responseCode = "404",
description = "活动不存在"
)
})
public ResponseEntity<Void> deleteActivity(@PathVariable Long id) {
// 实现逻辑
}
/**
* 获取排行榜
*/
@GetMapping("/{id}/leaderboard")
@Operation(
summary = "获取活动排行榜",
description = "获取指定活动的排行榜数据,支持分页",
tags = {"活动管理"},
operationId = "getLeaderboard"
)
@Parameters({
@Parameter(
name = "id",
description = "活动ID",
required = true
),
@Parameter(
name = "page",
description = "页码从0开始",
required = false,
schema = @Schema(type = "integer", defaultValue = "0")
),
@Parameter(
name = "size",
description = "每页大小",
required = false,
schema = @Schema(type = "integer", defaultValue = "20", maximum = "100")
),
@Parameter(
name = "topN",
description = "只显示前N名如果设置则忽略分页",
required = false,
schema = @Schema(type = "integer")
)
})
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "排行榜数据",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = LeaderboardResponse.class)
)
)
})
public ResponseEntity<ApiResponse<LeaderboardResponse>> getLeaderboard(
@PathVariable Long id,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) Integer topN) {
// 实现逻辑
}
}
```
### 2. 模型注解
```java
package com.mosquito.project.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
/**
* 活动创建请求
*/
@Data
@Schema(description = "活动创建请求")
public class CreateActivityRequest {
@Schema(
description = "活动名称",
example = "新年推广活动",
required = true
)
@NotBlank(message = "活动名称不能为空")
@Size(min = 2, max = 100, message = "活动名称长度必须在2-100个字符之间")
private String name;
@Schema(
description = "活动开始时间",
example = "2024-01-01T10:00:00",
required = true
)
@NotNull(message = "开始时间不能为空")
private LocalDateTime startTime;
@Schema(
description = "活动结束时间",
example = "2024-01-31T23:59:59",
required = true
)
@NotNull(message = "结束时间不能为空")
private LocalDateTime endTime;
@Schema(
description = "活动描述",
example = "新年期间的用户推广活动"
)
private String description;
@Schema(
description = "活动状态",
example = "draft",
allowableValues = {"draft", "active", "completed", "cancelled"}
)
private String status;
}
```
```java
package com.mosquito.project.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
/**
* 活动响应
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "活动响应")
public class Activity {
@Schema(description = "活动ID", example = "1")
private Long id;
@Schema(description = "活动名称", example = "新年推广活动")
private String name;
@Schema(description = "活动开始时间", example = "2024-01-01T10:00:00")
private LocalDateTime startTime;
@Schema(description = "活动结束时间", example = "2024-01-31T23:59:59")
private LocalDateTime endTime;
@Schema(description = "活动状态", example = "active")
private String status;
@Schema(description = "活动描述")
private String description;
@Schema(description = "创建时间", example = "2024-01-01T08:00:00")
private LocalDateTime createdAt;
@Schema(description = "更新时间", example = "2024-01-01T08:00:00")
private LocalDateTime updatedAt;
}
```
### 3. 枚举注解
```java
package com.mosquito.project.domain;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* 活动状态枚举
*/
@Schema(description = "活动状态")
public enum ActivityStatus {
@Schema(description = "草稿状态")
DRAFT("draft", "草稿"),
@Schema(description = "进行中")
ACTIVE("active", "进行中"),
@Schema(description = "已完成")
COMPLETED("completed", "已完成"),
@Schema(description = "已取消")
CANCELLED("cancelled", "已取消");
private final String code;
private final String description;
ActivityStatus(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
}
```
## 🌐 访问API文档
### Swagger UI
```
开发环境: http://localhost:8080/swagger-ui.html
测试环境: https://test-api.mosquito.com/swagger-ui.html
生产环境: https://api.mosquito.com/swagger-ui.html
```
### OpenAPI JSON
```
http://localhost:8080/api-docs
https://api.mosquito.com/api-docs
```
### OpenAPI YAML
```
http://localhost:8080/api-docs.yaml
https://api.mosquito.com/api-docs.yaml
```
## 🔒 安全配置
### 1. API密钥认证
```java
@SecurityRequirement(name = "API Key")
@Operation(summary = "需要API密钥的接口")
public ResponseEntity<?> securedEndpoint() {
// 实现逻辑
}
```
### 2. JWT认证
```java
@SecurityRequirement(name = "Bearer Auth")
@Operation(summary = "需要JWT Token的接口")
public ResponseEntity<?> jwtSecuredEndpoint() {
// 实现逻辑
}
```
### 3. 多重安全要求
```java
@Operation(
summary = "多种认证方式",
security = {
@SecurityRequirement(name = "API Key"),
@SecurityRequirement(name = "Bearer Auth")
}
)
public ResponseEntity<?> multipleAuthEndpoint() {
// 实现逻辑
}
```
## 📚 导出API文档
### 1. 导出JSON格式
```bash
curl -o openapi.json http://localhost:8080/api-docs
```
### 2. 导出YAML格式
```bash
curl -o openapi.yaml http://localhost:8080/api-docs.yaml
```
### 3. 使用OpenAPI Generator生成客户端
```bash
# 生成TypeScript客户端
openapi-generator-cli generate \
-i openapi.json \
-g typescript-axios \
-o ./client/typescript
# 生成Java客户端
openapi-generator-cli generate \
-i openapi.json \
-g java \
-o ./client/java
# 生成Python客户端
openapi-generator-cli generate \
-i openapi.json \
-g python \
-o ./client/python
```
## 🧪 测试API文档
### 1. 使用Swagger UI测试
```typescript
// 在浏览器中访问Swagger UI
// 1. 点击 "Authorize" 按钮
// 2. 输入API密钥: "your-api-key"
// 3. 点击 "Authorize"
// 4. 现在可以使用 "Try it out" 功能测试API
```
### 2. 使用Postman测试
```javascript
// Postman Pre-request Script
pm.request.headers.add({
key: 'X-API-Key',
value: 'your-api-key'
});
// 导入OpenAPI到Postman
// 1. 打开Postman
// 2. File -> Import
// 3. 选择 openapi.json 文件
// 4. Postman会自动创建Collection
```
## 🔧 自定义配置
### 1. 自定义响应示例
```java
@Operation(
summary = "创建活动",
responses = {
@ApiResponse(
responseCode = "201",
description = "活动创建成功",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Activity.class),
examples = @ExampleObject(
name = "示例响应",
value = "{\"id\":1,\"name\":\"新年推广活动\",\"status\":\"active\"}"
)
)
)
}
)
```
### 2. 自定义请求示例
```java
@Operation(
summary = "创建活动",
requestBody = @RequestBody(
description = "活动创建请求",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = CreateActivityRequest.class),
examples = {
@ExampleObject(
name = "标准请求",
value = "{\"name\":\"新年推广活动\",\"startTime\":\"2024-01-01T10:00:00\",\"endTime\":\"2024-01-31T23:59:59\"}"
)
}
)
)
)
```
### 3. 自定义错误响应
```java
@Operation(
summary = "创建活动",
responses = {
@ApiResponse(
responseCode = "400",
description = "请求参数错误",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(
name = "参数错误示例",
value = "{\"code\":\"VALIDATION_ERROR\",\"message\":\"活动名称不能为空\",\"details\":{\"name\":\"活动名称不能为空\"}}"
)
)
)
}
)
```
## 📊 API文档最佳实践
### 1. 分组组织
```java
@Tag(name = "活动管理", description = "活动相关的API接口")
@Tag(name = "用户管理", description = "用户相关的API接口")
@Tag(name = "分享功能", description = "分享相关的API接口")
```
### 2. 清晰的描述
```java
@Operation(
summary = "创建新活动", // 简短的标题
description = """
创建一个新的推广活动。
**注意事项:**
- 活动名称不能为空
- 开始时间必须早于结束时间
- 活动时长不能超过90天
""" // 详细的描述
)
```
### 3. 错误处理
```java
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "操作成功"),
@ApiResponse(responseCode = "400", description = "请求参数错误"),
@ApiResponse(responseCode = "401", description = "API密钥无效"),
@ApiResponse(responseCode = "403", description = "权限不足"),
@ApiResponse(responseCode = "404", description = "资源不存在"),
@ApiResponse(responseCode = "429", description = "请求过于频繁"),
@ApiResponse(responseCode = "500", description = "服务器内部错误")
})
```
## 🔄 自动化文档更新
### 1. CI/CD集成
```yaml
# .github/workflows/docs.yml
name: Update API Documentation
on:
push:
branches: [main]
jobs:
update-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate OpenAPI Spec
run: |
curl -o openapi.json http://localhost:8080/api-docs
curl -o openapi.yaml http://localhost:8080/api-docs.yaml
- name: Commit Documentation
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add openapi.json openapi.yaml
git commit -m "Update API documentation"
git push
```
### 2. 自动生成客户端SDK
```bash
#!/bin/bash
# generate-client-sdks.sh
# 生成TypeScript客户端
echo "Generating TypeScript client..."
openapi-generator-cli generate \
-i openapi.json \
-g typescript-axios \
-o ./client/typescript
# 生成Java客户端
echo "Generating Java client..."
openapi-generator-cli generate \
-i openapi.json \
-g java \
-o ./client/java
echo "Client SDKs generated successfully!"
```
---
*OpenAPI文档配置版本: v2.0.0*
*最后更新: 2026-01-22*
*维护团队: API Team*