docs: 创建 API 接口设计规范

This commit is contained in:
张翔
2026-03-08 21:44:07 +08:00
parent ad56253552
commit 7c1c9e5fe5
@@ -0,0 +1,588 @@
# 健身房管理系统 API 接口设计规范
> 文档编号:GYM-API-SPEC-001
> 版本:v1.0
> 创建日期:2026-03-08
> 最后更新日期:2026-03-08
> 作者:张翔
> 状态:正式发布
## 文档修订历史
| 版本 | 日期 | 作者 | 修订内容 |
| ---- | ---------- | ---- | -------- |
| v1.0 | 2026-03-08 | 张翔 | 创建 API 接口设计规范 |
## 参考文档
- RESTful API 最佳实践
- OpenAPI 3.0 规范
- Spring WebFlux 官方文档
- RSocket 规范
---
## 一、API 设计原则
### 1.1 RESTful 风格
**资源导向**
- 使用名词表示资源,不使用动词
- 使用 HTTP 方法表示操作
- 使用复数名词表示资源集合
**示例**
```
✅ GET /api/v1/members # 获取会员列表
✅ POST /api/v1/members # 创建会员
✅ GET /api/v1/members/{id} # 获取单个会员
✅ PUT /api/v1/members/{id} # 更新会员
✅ DELETE /api/v1/members/{id} # 删除会员
❌ GET /api/v1/getMembers
❌ POST /api/v1/createMember
```
### 1.2 版本控制
**URL 路径版本化**
```
/api/v1/members
/api/v2/members
```
**版本号规则**
- 格式:`v{主版本号}`
- 主版本号递增:不兼容的 API 变更
- 向后兼容:在同一版本内添加字段
### 1.3 响应式 API 设计
**异步非阻塞**
- 使用 Spring WebFlux 实现响应式 API
- 返回类型:`Mono<T>`(单个对象)、`Flux<T>`(集合)
- 支持 Server-Sent Events (SSE) 实时推送
**示例**
```java
// 单个资源
@GetMapping("/{id}")
public Mono<Member> getMember(@PathVariable Long id) {
return memberService.findById(id);
}
// 资源集合
@GetMapping
public Flux<Member> listMembers() {
return memberService.findAll();
}
// SSE 实时推送
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Member> streamMembers() {
return memberService.streamAll();
}
```
---
## 二、API 响应格式
### 2.1 标准响应结构
**成功响应**
```json
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"name": "张三",
"phone": "138****1234"
},
"timestamp": "2026-03-08T10:30:00Z"
}
```
**列表响应**
```json
{
"code": 200,
"message": "success",
"data": {
"items": [
{
"id": 1,
"name": "张三"
},
{
"id": 2,
"name": "李四"
}
],
"pagination": {
"page": 1,
"size": 20,
"total": 100,
"totalPages": 5
}
},
"timestamp": "2026-03-08T10:30:00Z"
}
```
**错误响应**
```json
{
"code": 400,
"message": "参数验证失败",
"errors": [
{
"field": "phone",
"message": "手机号格式不正确"
}
],
"timestamp": "2026-03-08T10:30:00Z"
}
```
### 2.2 HTTP 状态码
| 状态码 | 含义 | 使用场景 |
|--------|------|----------|
| 200 OK | 成功 | GET、PUT、PATCH 成功 |
| 201 Created | 已创建 | POST 成功创建资源 |
| 204 No Content | 无内容 | DELETE 成功 |
| 400 Bad Request | 请求错误 | 参数验证失败 |
| 401 Unauthorized | 未授权 | 未登录或 Token 过期 |
| 403 Forbidden | 禁止访问 | 权限不足 |
| 404 Not Found | 未找到 | 资源不存在 |
| 409 Conflict | 冲突 | 资源已存在 |
| 422 Unprocessable Entity | 不可处理 | 业务规则验证失败 |
| 429 Too Many Requests | 请求过多 | 触发限流 |
| 500 Internal Server Error | 服务器错误 | 系统异常 |
### 2.3 数据格式
**日期时间格式**
```
ISO 8601: 2026-03-08T10:30:00Z
日期:2026-03-08
时间:10:30:00
```
**数字格式**
```
金额:100.00 (DECIMAL)
数量:10 (INTEGER)
比例:0.15 (DECIMAL)
```
**布尔值**
```
true/false (JSON 原生布尔值)
```
---
## 三、API 接口分类
### 3.1 会员管理 API
#### 3.1.1 创建会员
```
POST /api/v1/members
```
**请求体**
```json
{
"phone": "13812341234",
"name": "张三",
"gender": 1,
"birthday": "1990-01-01",
"fitnessGoal": "增肌"
}
```
**响应**
```json
{
"code": 201,
"message": "创建成功",
"data": {
"id": 1,
"phone": "138****1234",
"name": "张三"
}
}
```
#### 3.1.2 获取会员详情
```
GET /api/v1/members/{id}
```
**响应**
```json
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"phone": "138****1234",
"name": "张三",
"gender": 1,
"birthday": "1990-01-01",
"fitnessGoal": "增肌",
"status": 1,
"level": 2
}
}
```
#### 3.1.3 会员列表查询
```
GET /api/v1/members
```
**查询参数**
```
?phone=13812341234&status=1&page=1&size=20&sort=createdAt,desc
```
**响应**
```json
{
"code": 200,
"message": "success",
"data": {
"items": [...],
"pagination": {
"page": 1,
"size": 20,
"total": 100,
"totalPages": 5
}
}
}
```
### 3.2 预约管理 API
#### 3.2.1 创建预约
```
POST /api/v1/bookings
```
**请求体**
```json
{
"slotId": 1,
"memberId": 1
}
```
**响应**
```json
{
"code": 201,
"message": "预约成功",
"data": {
"id": 1,
"bookingNo": "BK202603080001",
"status": 1
}
}
```
#### 3.2.2 取消预约
```
DELETE /api/v1/bookings/{id}
```
**响应**
```json
{
"code": 204,
"message": "取消成功"
}
```
### 3.3 订阅管理 API
#### 3.3.1 开通订阅模块
```
POST /api/v1/subscriptions
```
**请求体**
```json
{
"moduleCode": "marketing",
"billingCycle": 1,
"startDate": "2026-03-08",
"endDate": "2026-04-07"
}
```
**响应**
```json
{
"code": 201,
"message": "开通成功",
"data": {
"id": 1,
"subscriptionNo": "SUB202603080001",
"moduleCode": "marketing",
"status": 1
}
}
```
---
## 四、错误处理
### 4.1 错误码规范
**错误码结构**
```
业务码 (3 位) + 错误类型码 (2 位) + 具体错误码 (2 位)
```
**示例**
```
MEM0101 - 会员创建失败
MEM0102 - 会员手机号已存在
BOK0201 - 预约失败
BOK0202 - 预约时段已满
SUB0301 - 订阅开通失败
```
### 4.2 全局异常处理
**控制器建议**
```java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Mono<ApiResponse<Void>> handleResourceNotFound(
ResourceNotFoundException ex) {
return Mono.just(ApiResponse.error(404, ex.getMessage()));
}
@ExceptionHandler(ValidationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Mono<ApiResponse<Void>> handleValidationException(
ValidationException ex) {
return Mono.just(ApiResponse.error(400, ex.getMessage(), ex.getErrors()));
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Mono<ApiResponse<Void>> handleException(Exception ex) {
log.error("系统异常", ex);
return Mono.just(ApiResponse.error(500, "系统繁忙,请稍后重试"));
}
}
```
### 4.3 参数验证
**请求体验证**
```java
public class CreateMemberRequest {
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@NotBlank(message = "姓名不能为空")
@Size(min = 1, max = 50, message = "姓名长度不能超过 50 个字符")
private String name;
@NotNull(message = "性别不能为空")
@Min(value = 0, message = "性别值无效")
@Max(value = 2, message = "性别值无效")
private Integer gender;
// getters and setters
}
```
---
## 五、安全设计
### 5.1 认证机制
**JWT Token 认证**
```
Authorization: Bearer {token}
```
**Token 结构**
```json
{
"sub": "user123",
"tenantId": 1,
"storeId": 1,
"roles": ["ADMIN"],
"iat": 1709870400,
"exp": 1709956800
}
```
### 5.2 权限控制
**基于角色的访问控制 (RBAC)**
```java
@PreAuthorize("hasRole('ADMIN')")
@PostMapping
public Mono<Member> createMember(@RequestBody CreateMemberRequest request) {
return memberService.create(request);
}
@PreAuthorize("hasAnyRole('ADMIN', 'COACH')")
@GetMapping("/{id}")
public Mono<Member> getMember(@PathVariable Long id) {
return memberService.findById(id);
}
```
### 5.3 限流
**令牌桶限流**
```java
@RateLimiter(name = "apiRateLimiter")
@GetMapping
public Flux<Member> listMembers() {
return memberService.findAll();
}
```
**配置**
```yaml
resilience4j:
ratelimiter:
instances:
apiRateLimiter:
limit-for-period: 100 # 每次允许 100 个请求
limit-refresh-period: 1s # 每秒刷新
timeout-duration: 0 # 不等待
```
---
## 六、API 文档
### 6.1 OpenAPI 规范
**使用 Springdoc OpenAPI**
```java
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("健身房管理系统 API")
.version("v1")
.description("健身房管理系统 RESTful API 文档"))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.components(new Components()
.addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")));
}
```
### 6.2 API 文档访问
**Swagger UI**
```
http://localhost:8080/swagger-ui.html
```
**OpenAPI JSON**
```
http://localhost:8080/v3/api-docs
```
---
## 七、API 版本迁移
### 7.1 版本兼容策略
**向后兼容**
- 添加新字段:不影响旧版本
- 添加新接口:不影响旧版本
- 扩展枚举值:不影响旧版本
**不兼容变更**
- 删除字段:需要升级版本
- 修改字段类型:需要升级版本
- 修改业务逻辑:需要升级版本
### 7.2 版本废弃流程
1. **标记废弃**:在旧版本 API 添加 `@Deprecated` 注解
2. **通知用户**:通过文档、邮件通知升级
3. **过渡期**:至少保留 3 个月
4. **正式下线**:移除旧版本 API
---
## 八、性能优化
### 8.1 分页优化
**游标分页**
```
GET /api/v1/members?cursor=eyJpZCI6MTAwfQ==&size=20
```
**优势**
- 避免深度分页性能问题
- 适合大数据量场景
### 8.2 字段过滤
**按需返回字段**
```
GET /api/v1/members?fields=id,name,phone
```
**优势**
- 减少网络传输
- 提升响应速度
### 8.3 缓存策略
**HTTP 缓存头**
```
Cache-Control: max-age=3600, public
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 08 Mar 2026 10:30:00 GMT
```
**Redis 缓存**
```java
@Cacheable(value = "members", key = "#id")
public Mono<Member> findById(Long id) {
return memberRepository.findById(id);
}
```
---
**文档结束**