Files
gym-manage/docs/design/technical/API-接口设计规范.md
T
2026-03-08 21:44:07 +08:00

10 KiB
Raw Blame History

健身房管理系统 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) 实时推送

示例

// 单个资源
@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 标准响应结构

成功响应

{
  "code": 200,
  "message": "success",
  "data": {
    "id": 1,
    "name": "张三",
    "phone": "138****1234"
  },
  "timestamp": "2026-03-08T10:30:00Z"
}

列表响应

{
  "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"
}

错误响应

{
  "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

请求体

{
  "phone": "13812341234",
  "name": "张三",
  "gender": 1,
  "birthday": "1990-01-01",
  "fitnessGoal": "增肌"
}

响应

{
  "code": 201,
  "message": "创建成功",
  "data": {
    "id": 1,
    "phone": "138****1234",
    "name": "张三"
  }
}

3.1.2 获取会员详情

GET /api/v1/members/{id}

响应

{
  "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

响应

{
  "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

请求体

{
  "slotId": 1,
  "memberId": 1
}

响应

{
  "code": 201,
  "message": "预约成功",
  "data": {
    "id": 1,
    "bookingNo": "BK202603080001",
    "status": 1
  }
}

3.2.2 取消预约

DELETE /api/v1/bookings/{id}

响应

{
  "code": 204,
  "message": "取消成功"
}

3.3 订阅管理 API

3.3.1 开通订阅模块

POST /api/v1/subscriptions

请求体

{
  "moduleCode": "marketing",
  "billingCycle": 1,
  "startDate": "2026-03-08",
  "endDate": "2026-04-07"
}

响应

{
  "code": 201,
  "message": "开通成功",
  "data": {
    "id": 1,
    "subscriptionNo": "SUB202603080001",
    "moduleCode": "marketing",
    "status": 1
  }
}

四、错误处理

4.1 错误码规范

错误码结构

业务码 (3 位) + 错误类型码 (2 位) + 具体错误码 (2 位)

示例

MEM0101 - 会员创建失败
MEM0102 - 会员手机号已存在
BOK0201 - 预约失败
BOK0202 - 预约时段已满
SUB0301 - 订阅开通失败

4.2 全局异常处理

控制器建议

@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 参数验证

请求体验证

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 结构

{
  "sub": "user123",
  "tenantId": 1,
  "storeId": 1,
  "roles": ["ADMIN"],
  "iat": 1709870400,
  "exp": 1709956800
}

5.2 权限控制

基于角色的访问控制 (RBAC)

@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 限流

令牌桶限流

@RateLimiter(name = "apiRateLimiter")
@GetMapping
public Flux<Member> listMembers() {
    return memberService.findAll();
}

配置

resilience4j:
  ratelimiter:
    instances:
      apiRateLimiter:
        limit-for-period: 100  # 每次允许 100 个请求
        limit-refresh-period: 1s  # 每秒刷新
        timeout-duration: 0  # 不等待

六、API 文档

6.1 OpenAPI 规范

使用 Springdoc OpenAPI

@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 缓存

@Cacheable(value = "members", key = "#id")
public Mono<Member> findById(Long id) {
    return memberRepository.findById(id);
}

文档结束