893 lines
23 KiB
Markdown
893 lines
23 KiB
Markdown
|
|
# 🦟 蚊子项目 - 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*
|