@swagger JSDoc 注解Scalar + swagger-jsdoc + 覆盖率检测 + 角色 Token 注入
Celoria 后端使用 Scalar API Reference 作为 API 文档浏览器,通过 swagger-jsdoc 从源码中的 @swagger 注解自动生成 OpenAPI 3.0 规范。配套的 覆盖率检测脚本 确保每个端点都有文档,Token 自动注入 支持在文档页面快速切换角色测试 API。
http://localhost:3000/api-docs(本地开发环境)
graph TB
subgraph 源码层
A["backend/api/*.js (92个文件)"] -->|"@swagger 注解"| B[swagger-jsdoc]
C["swagger.config.js (全局配置)"] --> B
end
subgraph 运行时
B -->|生成| D["OpenAPI 3.0 Spec (/api-docs.json)"]
D --> E["Scalar API Reference (/api-docs)"]
F["scalar-login-inject.js (Token注入)"] --> E
end
subgraph 检测层
A --> G["check-api-docs.js (覆盖率检测)"]
D --> G
G -->|"API 端点"| H["/api/docs-coverage (实时接口)"]
H --> E
end
style A fill:#0f3460,stroke:#00d4ff,color:#eee
style D fill:#0f3460,stroke:#10b981,color:#eee
style E fill:#0f3460,stroke:#f59e0b,color:#eee
style G fill:#0f3460,stroke:#e94560,color:#eee
@swagger JSDoc 注解swagger-jsdoc 在后端启动时扫描所有 API 文件,合并生成完整的 OpenAPI 3.0 spec/api-docs.json 加载 spec,渲染为交互式文档页面scalar-login-inject.js 注入到页面,提供快速登录按钮和覆盖率检查入口/api/docs-coverage,后端实时执行检测脚本返回结果每个路由定义(router.get/post/put/delete)前必须添加 @swagger JSDoc 注释块。swagger-jsdoc 会自动扫描并提取这些注解。
/** * @swagger * /api/appointments/{id}: * get: * summary: 获取预约详情 * tags: [Appointments] * security: * - bearerAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: string * description: 预约 ID * responses: * 200: * description: 成功返回预约详情 * 401: * description: 未认证 * 404: * description: 预约不存在 * 500: * description: 服务器错误 */ router.get('/:id', async (req, res) => { ... });
| 字段 | 必填 | 说明 |
|---|---|---|
路径 | 是 | 完整的 API 路径,如 /api/appointments/{id},路径参数用 {param} 格式 |
HTTP 方法 | 是 | get / post / put / patch / delete |
summary | 是 | 端点简要描述(一句话) |
tags | 是 | 分类标签,决定在 Scalar 左侧栏的分组 |
security | 视情况 | 需要认证的端点添加 bearerAuth;公开端点(如注册登录)不加 |
parameters | 视情况 | 路径参数 / 查询参数 |
requestBody | 视情况 | POST/PUT 请求体 |
responses | 是 | 至少包含成功响应和主要错误码 |
api/appointments.js 中的 router.get('/:id'),注解路径应写 /api/appointments/{id},而不是 /{id}。
端点录入到文档系统是半自动的:注解需要手动编写,但扫描和发布是全自动的。
sequenceDiagram
participant Dev as 开发者
participant Code as API 文件
participant SJSD as swagger-jsdoc
participant Spec as OpenAPI Spec
participant Scalar as Scalar UI
Dev->>Code: 编写 @swagger 注解
Note over Dev,Code: 手动步骤
Dev->>Code: 重启后端 / 热重载
Code->>SJSD: 自动扫描所有 api/*.js
SJSD->>Spec: 生成 /api-docs.json
Scalar->>Spec: 加载 spec
Scalar->>Scalar: 渲染文档页面
Note over SJSD,Scalar: 全自动
// backend/swagger.config.js const options = { definition: { openapi: '3.0.0', info: { title: 'Celoria API 文档', version: '2.0.0', }, components: { securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer' }, guestBearerAuth: { type: 'http', scheme: 'bearer' }, } } }, apis: [ './api/**/*.js', // 所有 API 路由文件 './server.js', // server.js 中的内联路由 ], };
@swagger 注解写在被 apis glob 覆盖的文件中,后端启动时就会自动被收录到 OpenAPI spec 中。不需要额外注册或配置。
覆盖率检测脚本 scripts/check-api-docs.js 负责对比源码中的实际路由与 OpenAPI spec 中的文档化端点,计算覆盖率。
graph LR
A["扫描源码 (提取路由定义)"] --> B["解析挂载前缀 (组合完整路径)"]
B --> C["生成 OpenAPI Spec (文档化端点)"]
C --> D["逐一对比 (source vs spec)"]
D --> E["输出覆盖率报告 (未文档化列表)"]
style A fill:#0f3460,stroke:#00d4ff,color:#eee
style B fill:#0f3460,stroke:#f59e0b,color:#eee
style C fill:#0f3460,stroke:#10b981,color:#eee
style D fill:#0f3460,stroke:#e94560,color:#eee
style E fill:#0f3460,stroke:#00d4ff,color:#eee
脚本需要知道每个 API 文件的挂载前缀才能组合完整路径。采用三级策略逐步解析:
| 优先级 | 策略 | 来源 | 示例 |
|---|---|---|---|
| 1 (最高) | 解析 server.js 中的 app.use() |
app.use('/api/appointments', router) |
api/appointments.js → /api/appointments |
| 2 | 解析 index.js 子路由的 router.use() |
router.use('/calls', callsRouter) |
api/voice/calls.js → /api/voice/calls |
| 3 (兜底) | 从文件内的 @swagger 注解反推 |
对比注解路径与路由路径的差值 | 注解 /api/cart/active - 路由 /active = /api/cart |
$ npm run check:api-docs
File Total Documented Coverage
──────────────────────────────────────────────────────────────
admin/audit.js 6 6 100%
admin/permissions.js 13 13 100%
appointments.js 9 9 100%
guest-auth.js 13 13 100%
payment.js 32 32 100%
voice/calls.js 9 9 100%
...
──────────────────────────────────────────────────────────────
Total 660 660 100%
Scalar 页面右上角的 Coverage 按钮可以实时触发覆盖率检测:
GET /api/docs-coveragenode scripts/check-api-docs.js --json(实时扫描源码)# 覆盖率低于 95% 时 exit code 1,可用于 CI 流水线 npm run check:api-docs:strict # 自定义阈值 node scripts/check-api-docs.js --strict --threshold 90 # JSON 输出(供工具消费) node scripts/check-api-docs.js --json
Scalar 页面右上角提供三个快速登录按钮,一键获取不同角色的 JWT Token 用于 API 测试。
| 按钮 | 测试账号 | 角色 | 权限来源 |
|---|---|---|---|
| 管理员登录 | test.admin@qqnails.com |
admin | RBAC 系统中该账号被分配的权限 |
| 超级管理员 | test.superadmin@qqnails.com |
super_admin | 自动跳过权限检查(全部权限) |
| 客户登录 | guest@test.com |
guest | Guest Auth 系统,仅访问 /api/guest-* 端点 |
test.admin@qqnails.com 这个具体账号,它的 Token 权限由 RBAC 系统中该账号被分配的角色和权限决定。不同管理员账号可以有不同权限。只有超级管理员会自动跳过权限检查。
sequenceDiagram
participant User as 用户
participant UI as Scalar 页面
participant Inject as scalar-login-inject.js
participant API as 后端 API
User->>UI: 点击"管理员登录"
Inject->>API: GET /api/test-accounts
API-->>Inject: 返回测试账号信息
Inject->>API: POST /api/auth/employee/login
API-->>Inject: 返回 JWT Token
Inject->>Inject: 存储到 localStorage
Inject->>UI: 显示"已登录"状态
Note over User,UI: 后续在 Scalar 中测试 API 时需手动复制 Token
User->>UI: 点击"客户登录"
Inject->>API: POST /api/guest-auth/login
alt 账号不存在
API-->>Inject: 404
Inject->>API: POST /api/guest-auth/register (自动注册)
API-->>Inject: 返回 Guest Token
else 账号存在
API-->>Inject: 返回 Guest Token
end
Inject->>Inject: 存储 + 标记 token_type=guest
Token 保存在浏览器 localStorage 中,刷新页面后自动恢复登录状态:
| Key | 用途 |
|---|---|
scalar_token | JWT Token 字符串 |
scalar_user | 当前登录的用户名 |
scalar_token_type | Token 类型(guest 或空) |
三个按钮主要用于快速切换角色来测试 API 权限行为:
graph TD
A["编写路由代码 (router.get/post)"] --> B["添加 @swagger 注解"]
B --> C["重启后端"]
C --> D["访问 /api-docs 确认"]
D --> E["点击 Coverage 确认 100%"]
style A fill:#0f3460,stroke:#00d4ff,color:#eee
style B fill:#0f3460,stroke:#f59e0b,color:#eee
style C fill:#0f3460,stroke:#10b981,color:#eee
style D fill:#0f3460,stroke:#00d4ff,color:#eee
style E fill:#0f3460,stroke:#10b981,color:#eee
GET /api/xxx 缺文档@swagger 注解 → 重启 → 再次点击 Coverage 确认npm run check:api-docs:strict,覆盖率低于 95% 自动失败。防止团队成员提交未文档化的端点。
| 命令 | 说明 |
|---|---|
npm run check:api-docs |
运行覆盖率检查,输出表格报告 |
npm run check:api-docs:strict |
严格模式:覆盖率 < 95% 则 exit code 1(用于 CI) |
node scripts/check-api-docs.js --json |
JSON 格式输出(供工具消费) |
node scripts/check-api-docs.js --verbose |
详细模式:列出所有端点(含已文档化的) |
node scripts/check-api-docs.js --threshold 90 |
自定义阈值(默认 80%) |
curl localhost:3000/api-docs.json |
导出完整 OpenAPI spec(可导入 Postman) |
curl localhost:3000/api/docs-coverage |
获取覆盖率 JSON(和 Coverage 按钮一样的数据) |
http://localhost:3000/api-docs.json,即可导入全部 660 个端点的定义。