10 KiB
10 KiB
健身房管理系统 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 版本废弃流程
- 标记废弃:在旧版本 API 添加
@Deprecated注解 - 通知用户:通过文档、邮件通知升级
- 过渡期:至少保留 3 个月
- 正式下线:移除旧版本 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);
}
文档结束