feat: 添加管理后台页面和功能,优化测试和性能配置
refactor: 重构页面导航和滚动逻辑,提升用户体验 test: 更新测试配置和用例,增加覆盖率和稳定性 perf: 优化性能指标和阈值,适应开发环境需求 ci: 添加Lighthouse CI工作流,集成性能测试 docs: 更新API文档和健康检查端点 fix: 修复登录页面和表单提交问题 style: 调整响应式布局和可访问性改进 chore: 更新依赖项和脚本配置
This commit is contained in:
@@ -0,0 +1,445 @@
|
||||
# Allure 测试报告使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
Allure Framework 是一个灵活的、轻量级的、多语言测试报告工具,它不仅以简洁的Web报告形式展示测试结果,还提供了完整的测试执行历史记录。
|
||||
|
||||
## 安装状态
|
||||
|
||||
✅ **已安装组件**:
|
||||
- `allure-playwright`: ^3.5.0 (Playwright集成)
|
||||
- `allure-commandline`: ^2.37.0 (命令行工具)
|
||||
|
||||
✅ **已配置**:
|
||||
- Playwright配置文件中已集成Allure reporter
|
||||
- 分层测试配置支持Allure报告生成
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 运行测试并生成报告
|
||||
|
||||
```bash
|
||||
# 运行分层测试(会自动生成allure-results)
|
||||
npm run test:tier:fast
|
||||
npm run test:tier:standard
|
||||
npm run test:tier:deep
|
||||
|
||||
# 或者运行所有分层测试
|
||||
npm run test:tier:all
|
||||
```
|
||||
|
||||
### 2. 生成HTML报告
|
||||
|
||||
```bash
|
||||
# 生成Allure报告
|
||||
npm run test:allure
|
||||
|
||||
# 或者直接打开报告
|
||||
npm run test:allure:open
|
||||
|
||||
# 或者实时serve报告
|
||||
npm run test:allure:serve
|
||||
```
|
||||
|
||||
## 报告功能
|
||||
|
||||
### 📊 测试概览
|
||||
|
||||
Allure报告提供以下信息:
|
||||
|
||||
1. **测试统计**
|
||||
- 总测试数
|
||||
- 通过/失败/跳过数量
|
||||
- 成功率
|
||||
- 执行时间
|
||||
|
||||
2. **测试分类**
|
||||
- 按套件分组
|
||||
- 按标签分组(@smoke, @regression等)
|
||||
- 按严重程度分组
|
||||
|
||||
3. **历史趋势**
|
||||
- 测试结果历史对比
|
||||
- 失败率趋势
|
||||
- 执行时间趋势
|
||||
|
||||
### 📈 详细信息
|
||||
|
||||
每个测试用例包含:
|
||||
|
||||
- **测试步骤**: 详细的执行步骤
|
||||
- **附件**: 截图、视频、日志
|
||||
- **参数**: 测试参数和配置
|
||||
- **时间线**: 执行时间分布
|
||||
- **环境信息**: 浏览器、操作系统等
|
||||
|
||||
### 🎯 测试分层支持
|
||||
|
||||
分层测试配置已集成Allure报告:
|
||||
|
||||
| 测试层级 | 配置文件 | 报告输出 |
|
||||
|---------|---------|---------|
|
||||
| Fast | playwright.config.tiered.ts | allure-results/ |
|
||||
| Standard | playwright.config.tiered.ts | allure-results/ |
|
||||
| Deep | playwright.config.tiered.ts | allure-results/ |
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 场景1: 本地开发调试
|
||||
|
||||
```bash
|
||||
# 1. 运行特定测试
|
||||
cd e2e
|
||||
npx playwright test --grep "contact-form"
|
||||
|
||||
# 2. 实时查看报告
|
||||
npm run test:allure:serve
|
||||
```
|
||||
|
||||
### 场景2: CI/CD集成
|
||||
|
||||
```yaml
|
||||
# .woodpecker.yml示例
|
||||
pipeline:
|
||||
test:
|
||||
image: node:18
|
||||
commands:
|
||||
- npm run test:tier:fast
|
||||
- npm run test:tier:standard
|
||||
- npm run test:allure
|
||||
when:
|
||||
event: [push, pull_request]
|
||||
|
||||
publish-report:
|
||||
image: node:18
|
||||
commands:
|
||||
- allure generate allure-results -o allure-report
|
||||
when:
|
||||
status: [success, failure]
|
||||
```
|
||||
|
||||
### 场景3: 测试失败分析
|
||||
|
||||
```bash
|
||||
# 1. 运行失败的测试
|
||||
cd e2e
|
||||
npx playwright test --last-failed
|
||||
|
||||
# 2. 查看详细报告
|
||||
npm run test:allure:open
|
||||
|
||||
# 3. 在报告中查看:
|
||||
# - 失败的断言
|
||||
# - 错误堆栈
|
||||
# - 截图和视频
|
||||
# - 执行日志
|
||||
```
|
||||
|
||||
## 报告定制
|
||||
|
||||
### 添加测试标签
|
||||
|
||||
在测试文件中添加标签:
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('用户登录测试 @smoke @critical', async ({ page }) => {
|
||||
// 测试代码
|
||||
});
|
||||
```
|
||||
|
||||
### 添加测试步骤
|
||||
|
||||
```typescript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('复杂测试流程', async ({ page }) => {
|
||||
await test.step('打开登录页面', async () => {
|
||||
await page.goto('/login');
|
||||
});
|
||||
|
||||
await test.step('输入用户名', async () => {
|
||||
await page.fill('#username', 'test@example.com');
|
||||
});
|
||||
|
||||
await test.step('输入密码', async () => {
|
||||
await page.fill('#password', 'password123');
|
||||
});
|
||||
|
||||
await test.step('提交表单', async () => {
|
||||
await page.click('#submit');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 添加附件
|
||||
|
||||
```typescript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('带附件的测试', async ({ page }, testInfo) => {
|
||||
await page.goto('/');
|
||||
|
||||
// 添加截图
|
||||
const screenshot = await page.screenshot();
|
||||
await testInfo.attach('首页截图', {
|
||||
body: screenshot,
|
||||
contentType: 'image/png'
|
||||
});
|
||||
|
||||
// 添加文本日志
|
||||
await testInfo.attach('测试日志', {
|
||||
body: '这是测试日志内容',
|
||||
contentType: 'text/plain'
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 报告分析技巧
|
||||
|
||||
### 1. 识别不稳定测试
|
||||
|
||||
在报告的"Categories"部分查看:
|
||||
- 标记为"Product defects"的测试:真正的bug
|
||||
- 标记为"Test defects"的测试:测试代码问题
|
||||
|
||||
### 2. 性能分析
|
||||
|
||||
在"Timeline"标签页:
|
||||
- 查看测试执行时间分布
|
||||
- 识别慢速测试
|
||||
- 优化测试性能
|
||||
|
||||
### 3. 失败模式分析
|
||||
|
||||
在"Graphs"标签页:
|
||||
- 查看失败率趋势
|
||||
- 识别常见失败原因
|
||||
- 追踪测试稳定性
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### ✅ 推荐做法
|
||||
|
||||
1. **使用有意义的测试名称**
|
||||
```typescript
|
||||
test('用户应该能够成功登录 @smoke', async ({ page }) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
2. **添加详细的测试步骤**
|
||||
- 每个关键操作都是一个step
|
||||
- 步骤名称清晰描述操作
|
||||
|
||||
3. **为失败测试添加附件**
|
||||
- 失败时自动截图
|
||||
- 保存页面HTML
|
||||
- 记录控制台日志
|
||||
|
||||
4. **使用标签分类测试**
|
||||
- @smoke: 冒烟测试
|
||||
- @regression: 回归测试
|
||||
- @critical: 关键测试
|
||||
- @flaky: 不稳定测试
|
||||
|
||||
### ❌ 避免的做法
|
||||
|
||||
1. **不要在报告中包含敏感信息**
|
||||
- 密码
|
||||
- API密钥
|
||||
- 个人数据
|
||||
|
||||
2. **不要过度使用附件**
|
||||
- 只在必要时添加
|
||||
- 避免报告过大
|
||||
|
||||
3. **不要忽略失败测试**
|
||||
- 及时修复或标记
|
||||
- 分析根本原因
|
||||
|
||||
## CI/CD集成示例
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: Test with Allure Report
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm ci
|
||||
cd e2e && npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
npm run test:tier:fast
|
||||
npm run test:tier:standard
|
||||
|
||||
- name: Generate Allure Report
|
||||
if: always()
|
||||
run: npm run test:allure
|
||||
|
||||
- name: Upload Allure Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: allure-report
|
||||
path: e2e/allure-report
|
||||
```
|
||||
|
||||
### Woodpecker CI
|
||||
|
||||
```yaml
|
||||
pipeline:
|
||||
install:
|
||||
image: node:18
|
||||
commands:
|
||||
- npm ci
|
||||
- cd e2e && npm ci
|
||||
|
||||
test:
|
||||
image: node:18
|
||||
commands:
|
||||
- npm run test:tier:fast
|
||||
- npm run test:tier:standard
|
||||
when:
|
||||
event: [push, pull_request]
|
||||
|
||||
report:
|
||||
image: node:18
|
||||
commands:
|
||||
- cd e2e && npm run test:allure
|
||||
when:
|
||||
status: [success, failure]
|
||||
|
||||
publish:
|
||||
image: node:18
|
||||
commands:
|
||||
- cd e2e && npm run test:allure:open
|
||||
when:
|
||||
status: [success, failure]
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题1: 报告无法生成
|
||||
|
||||
**症状**: 运行`npm run test:allure`时报错
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查allure-commandline是否安装
|
||||
cd e2e
|
||||
npm ls allure-commandline
|
||||
|
||||
# 如果未安装,重新安装
|
||||
npm install --save-dev allure-commandline
|
||||
```
|
||||
|
||||
### 问题2: 报告内容为空
|
||||
|
||||
**症状**: 报告生成成功,但没有测试数据
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查allure-results目录
|
||||
ls -la e2e/allure-results
|
||||
|
||||
# 确保测试已运行
|
||||
npm run test:tier:fast
|
||||
```
|
||||
|
||||
### 问题3: 截图未显示
|
||||
|
||||
**症状**: 报告中看不到截图
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 确保Playwright配置中启用了截图
|
||||
use: {
|
||||
screenshot: 'only-on-failure', // 或 'on'
|
||||
video: 'retain-on-failure',
|
||||
trace: 'retain-on-failure',
|
||||
}
|
||||
```
|
||||
|
||||
## 进阶功能
|
||||
|
||||
### 1. 自定义报告配置
|
||||
|
||||
创建`allure.config.js`:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
resultsDir: 'allure-results',
|
||||
reportDir: 'allure-report',
|
||||
cleanResultsDir: true,
|
||||
cleanReportDir: true,
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 集成到测试框架
|
||||
|
||||
```typescript
|
||||
// playwright.config.ts
|
||||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
reporter: [
|
||||
['allure-playwright', {
|
||||
outputFolder: 'allure-results',
|
||||
detail: true,
|
||||
suiteTitle: false,
|
||||
}],
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### 3. 报告历史记录
|
||||
|
||||
使用Allure TestOps或Allure Report Server保存历史报告:
|
||||
|
||||
```bash
|
||||
# 安装Allure Report Server
|
||||
npm install -g allure-report-server
|
||||
|
||||
# 启动服务器
|
||||
allure-report-server --port 8080
|
||||
```
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [Allure官方文档](https://docs.qameta.io/allure/)
|
||||
- [Allure Playwright集成](https://github.com/allure-framework/allure-js)
|
||||
- [Playwright测试最佳实践](https://playwright.dev/docs/best-practices)
|
||||
- [测试分层指南](./test-tiering-best-practices.md)
|
||||
|
||||
## 总结
|
||||
|
||||
Allure测试报告已完全集成到项目中,提供了:
|
||||
|
||||
✅ **自动化报告生成**
|
||||
✅ **分层测试支持**
|
||||
✅ **丰富的测试信息**
|
||||
✅ **历史趋势分析**
|
||||
✅ **CI/CD集成**
|
||||
|
||||
通过合理使用Allure报告,可以:
|
||||
- 快速定位测试失败原因
|
||||
- 追踪测试稳定性
|
||||
- 分析测试性能
|
||||
- 提升测试质量
|
||||
@@ -0,0 +1,512 @@
|
||||
# API版本控制指南
|
||||
|
||||
## 概述
|
||||
|
||||
API版本控制是API设计的重要部分,它允许我们在不破坏现有客户端的情况下演进API。本项目采用URL路径版本控制策略。
|
||||
|
||||
## 版本控制策略
|
||||
|
||||
### URL路径版本控制
|
||||
|
||||
使用URL路径中的版本号来区分不同版本的API:
|
||||
|
||||
```
|
||||
/api/v1/endpoint # 版本1
|
||||
/api/v2/endpoint # 版本2
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- ✅ 清晰明了,易于理解
|
||||
- ✅ 便于缓存和路由
|
||||
- ✅ 支持多版本并存
|
||||
- ✅ 客户端易于使用
|
||||
|
||||
**缺点**:
|
||||
- ❌ URL较长
|
||||
- ❌ 需要维护多个版本
|
||||
|
||||
### 版本命名规则
|
||||
|
||||
- **主版本号**:`v1`, `v2`, `v3`...
|
||||
- **格式**:`/api/v{major}/`
|
||||
- **示例**:
|
||||
- `/api/v1/content`
|
||||
- `/api/v1/admin/users`
|
||||
|
||||
## 目录结构
|
||||
|
||||
### 当前结构(向后兼容)
|
||||
|
||||
```
|
||||
src/app/api/
|
||||
├── admin/
|
||||
│ ├── config/
|
||||
│ ├── content/
|
||||
│ ├── upload/
|
||||
│ └── users/
|
||||
├── auth/
|
||||
├── config/
|
||||
├── content/
|
||||
├── docs/
|
||||
└── health/
|
||||
```
|
||||
|
||||
### 版本化结构(推荐)
|
||||
|
||||
```
|
||||
src/app/api/
|
||||
├── v1/ # 版本1 API
|
||||
│ ├── admin/
|
||||
│ │ ├── config/
|
||||
│ │ ├── content/
|
||||
│ │ ├── upload/
|
||||
│ │ └── users/
|
||||
│ ├── auth/
|
||||
│ ├── config/
|
||||
│ ├── content/
|
||||
│ └── health/
|
||||
├── admin/ # 向后兼容(重定向到v1)
|
||||
├── auth/
|
||||
├── config/
|
||||
├── content/
|
||||
├── docs/ # OpenAPI文档(无版本)
|
||||
└── health/
|
||||
```
|
||||
|
||||
## 实施步骤
|
||||
|
||||
### 步骤1:创建版本化API
|
||||
|
||||
#### 创建v1目录
|
||||
|
||||
```bash
|
||||
mkdir -p src/app/api/v1
|
||||
```
|
||||
|
||||
#### 迁移现有API
|
||||
|
||||
将现有API复制到v1目录:
|
||||
|
||||
```bash
|
||||
# 复制admin API
|
||||
cp -r src/app/api/admin src/app/api/v1/
|
||||
|
||||
# 复制其他API
|
||||
cp -r src/app/api/auth src/app/api/v1/
|
||||
cp -r src/app/api/config src/app/api/v1/
|
||||
cp -r src/app/api/content src/app/api/v1/
|
||||
cp -r src/app/api/health src/app/api/v1/
|
||||
```
|
||||
|
||||
### 步骤2:更新API路由
|
||||
|
||||
#### 更新v1 API路由
|
||||
|
||||
在v1版本的API中,更新路由路径:
|
||||
|
||||
```typescript
|
||||
// src/app/api/v1/admin/content/route.ts
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/admin/content:
|
||||
* get:
|
||||
* tags:
|
||||
* - Admin
|
||||
* - Content
|
||||
* summary: 获取内容列表 (v1)
|
||||
* description: 管理员获取内容列表,支持分页、筛选和搜索
|
||||
* operationId: getAdminContentV1
|
||||
* ...
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
// 实现代码
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤3:创建向后兼容层
|
||||
|
||||
#### 创建重定向中间件
|
||||
|
||||
```typescript
|
||||
// src/middleware.ts
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
// 如果访问旧API路径,重定向到v1版本
|
||||
const legacyApiPaths = [
|
||||
'/api/admin',
|
||||
'/api/auth',
|
||||
'/api/config',
|
||||
'/api/content',
|
||||
'/api/health',
|
||||
];
|
||||
|
||||
const isLegacyApi = legacyApiPaths.some(path =>
|
||||
pathname.startsWith(path) && !pathname.includes('/v1/')
|
||||
);
|
||||
|
||||
if (isLegacyApi) {
|
||||
const url = request.nextUrl.clone();
|
||||
url.pathname = pathname.replace('/api/', '/api/v1/');
|
||||
|
||||
// 返回重定向响应(可选:也可以内部重写)
|
||||
// return NextResponse.redirect(url);
|
||||
|
||||
// 或者内部重写(URL不变,但使用新路径)
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: '/api/:path*',
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤4:更新客户端代码
|
||||
|
||||
#### 更新API客户端
|
||||
|
||||
```typescript
|
||||
// src/lib/api-client.ts
|
||||
|
||||
const API_VERSION = 'v1';
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || '';
|
||||
|
||||
export class ApiClient {
|
||||
private baseUrl: string;
|
||||
|
||||
constructor(version: string = API_VERSION) {
|
||||
this.baseUrl = `${API_BASE_URL}/api/${version}`;
|
||||
}
|
||||
|
||||
async get(endpoint: string, options?: RequestInit) {
|
||||
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
||||
...options,
|
||||
method: 'GET',
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async post(endpoint: string, data: any, options?: RequestInit) {
|
||||
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers,
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
const apiClient = new ApiClient('v1');
|
||||
const content = await apiClient.get('/admin/content');
|
||||
```
|
||||
|
||||
## 版本生命周期
|
||||
|
||||
### 版本状态
|
||||
|
||||
| 状态 | 描述 | 持续时间 |
|
||||
|------|------|----------|
|
||||
| **Current** | 当前推荐版本 | 无限期 |
|
||||
| **Supported** | 仍受支持,但不推荐新功能 | 6-12个月 |
|
||||
| **Deprecated** | 即将废弃,计划移除 | 3-6个月 |
|
||||
| **Sunset** | 已移除,不再可用 | - |
|
||||
|
||||
### 版本废弃流程
|
||||
|
||||
1. **公告**:提前6个月通知废弃计划
|
||||
2. **警告**:在响应头中添加`Deprecation`和`Sunset`头
|
||||
3. **迁移期**:提供迁移指南和工具
|
||||
4. **移除**:在预定日期移除旧版本
|
||||
|
||||
#### 添加废弃头
|
||||
|
||||
```typescript
|
||||
// src/app/api/v1/admin/content/route.ts
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const response = NextResponse.json(data);
|
||||
|
||||
// 添加废弃警告
|
||||
response.headers.set('Deprecation', 'true');
|
||||
response.headers.set('Sunset', 'Sat, 31 Dec 2026 23:59:59 GMT');
|
||||
response.headers.set('Link', '</api/v2/admin/content>; rel="successor-version"');
|
||||
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
## 版本间差异处理
|
||||
|
||||
### 向后兼容的变更
|
||||
|
||||
以下变更不需要增加主版本号:
|
||||
|
||||
- ✅ 添加新的可选参数
|
||||
- ✅ 添加新的响应字段
|
||||
- ✅ 添加新的端点
|
||||
- ✅ 修复bug
|
||||
|
||||
### 需要新版本的变更
|
||||
|
||||
以下变更需要增加主版本号:
|
||||
|
||||
- ❌ 移除或重命名端点
|
||||
- ❌ 移除或重命名请求/响应字段
|
||||
- ❌ 修改必填参数
|
||||
- ❌ 修改认证方式
|
||||
- ❌ 修改错误响应格式
|
||||
|
||||
## 多版本并存示例
|
||||
|
||||
### 场景:修改内容API响应格式
|
||||
|
||||
#### v1版本(旧)
|
||||
|
||||
```typescript
|
||||
// src/app/api/v1/admin/content/route.ts
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/admin/content:
|
||||
* get:
|
||||
* responses:
|
||||
* 200:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* items:
|
||||
* type: array
|
||||
* pagination:
|
||||
* type: object
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const items = await db.select().from(content);
|
||||
|
||||
return NextResponse.json({
|
||||
items,
|
||||
pagination: { page: 1, limit: 20, total: items.length },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### v2版本(新)
|
||||
|
||||
```typescript
|
||||
// src/app/api/v2/admin/content/route.ts
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v2/admin/content:
|
||||
* get:
|
||||
* responses:
|
||||
* 200:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* data:
|
||||
* type: array
|
||||
* meta:
|
||||
* type: object
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const items = await db.select().from(content);
|
||||
|
||||
return NextResponse.json({
|
||||
data: items, // 改名:items -> data
|
||||
meta: { // 改名:pagination -> meta
|
||||
page: 1,
|
||||
limit: 20,
|
||||
total: items.length,
|
||||
hasNext: items.length === 20,
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 测试策略
|
||||
|
||||
### 版本兼容性测试
|
||||
|
||||
```typescript
|
||||
// src/app/api/__tests__/version-compatibility.test.ts
|
||||
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
|
||||
describe('API Version Compatibility', () => {
|
||||
it('should return same data structure in v1 and v2', async () => {
|
||||
const v1Response = await fetch('/api/v1/admin/content');
|
||||
const v2Response = await fetch('/api/v2/admin/content');
|
||||
|
||||
const v1Data = await v1Response.json();
|
||||
const v2Data = await v2Response.json();
|
||||
|
||||
// 验证数据一致性
|
||||
expect(v1Data.items.length).toBe(v2Data.data.length);
|
||||
expect(v1Data.pagination.total).toBe(v2Data.meta.total);
|
||||
});
|
||||
|
||||
it('should redirect legacy API to v1', async () => {
|
||||
const response = await fetch('/api/admin/content');
|
||||
expect(response.url).toContain('/api/v1/admin/content');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 文档更新
|
||||
|
||||
### 更新OpenAPI文档
|
||||
|
||||
```typescript
|
||||
// src/app/api/docs/route.ts
|
||||
|
||||
const options = {
|
||||
definition: {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
title: '睿新致远 API',
|
||||
version: '1.0.0',
|
||||
description: `
|
||||
## API版本
|
||||
|
||||
当前支持以下版本:
|
||||
|
||||
- **v1** (Current): 当前推荐版本
|
||||
- **v2** (Beta): 测试版本,包含新功能
|
||||
|
||||
### 版本状态
|
||||
|
||||
| 版本 | 状态 | 发布日期 | 废弃日期 |
|
||||
|------|------|----------|----------|
|
||||
| v1 | Current | 2024-01-01 | - |
|
||||
| v2 | Beta | 2024-06-01 | - |
|
||||
`,
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: '/api/v1',
|
||||
description: 'API v1 (Current)',
|
||||
},
|
||||
{
|
||||
url: '/api/v2',
|
||||
description: 'API v2 (Beta)',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### ✅ 推荐做法
|
||||
|
||||
1. **提前规划版本策略**
|
||||
- 在API设计初期就考虑版本控制
|
||||
- 为未来变更预留空间
|
||||
|
||||
2. **保持向后兼容**
|
||||
- 尽可能保持旧版本可用
|
||||
- 提供充足的迁移时间
|
||||
|
||||
3. **清晰的文档**
|
||||
- 明确标注版本差异
|
||||
- 提供迁移指南
|
||||
|
||||
4. **版本废弃通知**
|
||||
- 提前通知用户
|
||||
- 使用HTTP头传递废弃信息
|
||||
|
||||
### ❌ 避免的做法
|
||||
|
||||
1. **不要频繁变更主版本**
|
||||
- 主版本变更应该谨慎
|
||||
- 考虑向后兼容的替代方案
|
||||
|
||||
2. **不要突然移除旧版本**
|
||||
- 给用户足够的迁移时间
|
||||
- 提供迁移工具和文档
|
||||
|
||||
3. **不要忽略版本测试**
|
||||
- 确保多版本并存时功能正常
|
||||
- 测试版本兼容性
|
||||
|
||||
## 监控和分析
|
||||
|
||||
### 版本使用统计
|
||||
|
||||
```typescript
|
||||
// src/lib/api-analytics.ts
|
||||
|
||||
export async function trackApiVersion(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
const version = pathname.match(/\/api\/v(\d+)\//)?.[1] || 'legacy';
|
||||
|
||||
// 发送到分析服务
|
||||
await analytics.track('api_request', {
|
||||
version,
|
||||
endpoint: pathname,
|
||||
method: request.method,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 版本使用报告
|
||||
|
||||
定期生成版本使用报告:
|
||||
|
||||
```markdown
|
||||
## API版本使用报告(2024年6月)
|
||||
|
||||
### 请求分布
|
||||
|
||||
| 版本 | 请求数 | 占比 | 趋势 |
|
||||
|------|--------|------|------|
|
||||
| v1 | 150,000 | 75% | ↓ |
|
||||
| v2 | 50,000 | 25% | ↑ |
|
||||
|
||||
### 废弃版本使用
|
||||
|
||||
| 版本 | 请求数 | 废弃日期 | 建议 |
|
||||
|------|--------|----------|------|
|
||||
| legacy | 1,000 | 2024-12-31 | 尽快迁移到v1 |
|
||||
```
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [API版本控制最佳实践](https://www.postman.com/api-platform/api-versioning/)
|
||||
- [REST API版本控制](https://restfulapi.net/versioning/)
|
||||
- [语义化版本控制](https://semver.org/)
|
||||
- [HTTP废弃头规范](https://datatracker.ietf.org/doc/html/rfc8594)
|
||||
|
||||
## 总结
|
||||
|
||||
API版本控制已集成到项目中,提供了:
|
||||
|
||||
✅ **清晰的版本管理**
|
||||
✅ **向后兼容支持**
|
||||
✅ **平滑的版本迁移**
|
||||
✅ **版本使用监控**
|
||||
✅ **完善的文档支持**
|
||||
|
||||
通过合理的版本控制策略,可以:
|
||||
- 保护现有客户端
|
||||
- 安全地演进API
|
||||
- 提供良好的开发者体验
|
||||
- 维护API的长期健康
|
||||
@@ -0,0 +1,513 @@
|
||||
# Lighthouse CI使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
Lighthouse CI是一个用于自动化性能测试的工具,它可以在CI/CD流程中运行Lighthouse审计,跟踪性能指标变化,并设置性能预算。
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
npm install --save-dev @lhci/cli
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
### 配置文件
|
||||
|
||||
项目根目录下的`lighthouserc.json`文件包含所有配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"numberOfRuns": 3,
|
||||
"startServerCommand": "npm run start",
|
||||
"startServerReadyPattern": "Local:",
|
||||
"url": [
|
||||
"http://localhost:3000/",
|
||||
"http://localhost:3000/about",
|
||||
"http://localhost:3000/services"
|
||||
],
|
||||
"settings": {
|
||||
"preset": "desktop",
|
||||
"onlyCategories": [
|
||||
"performance",
|
||||
"accessibility",
|
||||
"best-practices",
|
||||
"seo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"assert": {
|
||||
"assertions": {
|
||||
"categories:performance": ["error", {"minScore": 0.9}],
|
||||
"categories:accessibility": ["error", {"minScore": 0.9}],
|
||||
"categories:best-practices": ["error", {"minScore": 0.9}],
|
||||
"categories:seo": ["error", {"minScore": 0.9}]
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
"target": "temporary-public-storage"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 配置说明
|
||||
|
||||
#### collect配置
|
||||
|
||||
- **numberOfRuns**: 每个URL运行的次数(默认3次)
|
||||
- **startServerCommand**: 启动服务器的命令
|
||||
- **startServerReadyPattern**: 服务器就绪的匹配模式
|
||||
- **url**: 要测试的URL列表
|
||||
- **settings**: Lighthouse设置
|
||||
- **preset**: 测试预设(desktop/mobile)
|
||||
- **onlyCategories**: 只测试指定类别
|
||||
|
||||
#### assert配置
|
||||
|
||||
- **assertions**: 性能断言
|
||||
- **categories:performance**: 性能分数最低0.9
|
||||
- **categories:accessibility**: 可访问性分数最低0.9
|
||||
- **categories:best-practices**: 最佳实践分数最低0.9
|
||||
- **categories:seo**: SEO分数最低0.9
|
||||
|
||||
#### upload配置
|
||||
|
||||
- **target**: 上传目标
|
||||
- **temporary-public-storage**: 临时公共存储
|
||||
- **lhci**: Lighthouse CI服务器
|
||||
- **filesystem**: 本地文件系统
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 本地运行
|
||||
|
||||
#### 运行完整测试
|
||||
|
||||
```bash
|
||||
npm run lighthouse
|
||||
```
|
||||
|
||||
这将执行以下步骤:
|
||||
1. 启动开发服务器
|
||||
2. 收集性能数据
|
||||
3. 运行断言检查
|
||||
4. 上传结果
|
||||
|
||||
#### 分步运行
|
||||
|
||||
```bash
|
||||
# 1. 收集性能数据
|
||||
npm run lighthouse:collect
|
||||
|
||||
# 2. 运行断言检查
|
||||
npm run lighthouse:assert
|
||||
|
||||
# 3. 上传结果
|
||||
npm run lighthouse:upload
|
||||
```
|
||||
|
||||
#### 指定设备类型
|
||||
|
||||
```bash
|
||||
# 桌面端测试
|
||||
npm run lighthouse:desktop
|
||||
|
||||
# 移动端测试
|
||||
npm run lighthouse:mobile
|
||||
```
|
||||
|
||||
### CI/CD集成
|
||||
|
||||
#### GitHub Actions
|
||||
|
||||
创建`.github/workflows/lighthouse.yml`:
|
||||
|
||||
```yaml
|
||||
name: Lighthouse CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
lighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build application
|
||||
run: npm run build
|
||||
|
||||
- name: Run Lighthouse CI
|
||||
run: npm run lighthouse
|
||||
env:
|
||||
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
|
||||
|
||||
- name: Upload Lighthouse results
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: lighthouse-results
|
||||
path: lighthouse-reports
|
||||
```
|
||||
|
||||
#### GitLab CI
|
||||
|
||||
创建`.gitlab-ci.yml`:
|
||||
|
||||
```yaml
|
||||
lighthouse:
|
||||
stage: test
|
||||
image: node:18
|
||||
script:
|
||||
- npm ci
|
||||
- npm run build
|
||||
- npm run lighthouse
|
||||
artifacts:
|
||||
paths:
|
||||
- lighthouse-reports/
|
||||
expire_in: 1 week
|
||||
only:
|
||||
- main
|
||||
- merge_requests
|
||||
```
|
||||
|
||||
## 性能指标
|
||||
|
||||
### Core Web Vitals
|
||||
|
||||
Lighthouse CI重点监控以下Core Web Vitals指标:
|
||||
|
||||
| 指标 | 名称 | 目标值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| **LCP** | Largest Contentful Paint | < 2.5s | 最大内容绘制时间 |
|
||||
| **FID** | First Input Delay | < 100ms | 首次输入延迟 |
|
||||
| **CLS** | Cumulative Layout Shift | < 0.1 | 累积布局偏移 |
|
||||
|
||||
### 其他重要指标
|
||||
|
||||
| 指标 | 名称 | 目标值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| **FCP** | First Contentful Paint | < 1.8s | 首次内容绘制时间 |
|
||||
| **SI** | Speed Index | < 3.4s | 速度指数 |
|
||||
| **TBT** | Total Blocking Time | < 200ms | 总阻塞时间 |
|
||||
|
||||
### 性能预算
|
||||
|
||||
在`lighthouserc.json`中设置性能预算:
|
||||
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"assert": {
|
||||
"assertions": {
|
||||
"first-contentful-paint": ["error", {"maxNumericValue": 2000}],
|
||||
"largest-contentful-paint": ["error", {"maxNumericValue": 3000}],
|
||||
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
|
||||
"total-blocking-time": ["error", {"maxNumericValue": 300}],
|
||||
"speed-index": ["error", {"maxNumericValue": 3000}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 1. 减少LCP时间
|
||||
|
||||
**问题**:LCP > 2.5s
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 优化图片加载
|
||||
<img
|
||||
src="/hero.jpg"
|
||||
alt="Hero image"
|
||||
loading="eager"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
|
||||
// 使用Next.js Image组件
|
||||
<Image
|
||||
src="/hero.jpg"
|
||||
alt="Hero image"
|
||||
priority
|
||||
width={1920}
|
||||
height={1080}
|
||||
/>
|
||||
```
|
||||
|
||||
### 2. 减少CLS
|
||||
|
||||
**问题**:CLS > 0.1
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 为图片预留空间
|
||||
<img
|
||||
src="/image.jpg"
|
||||
alt="Description"
|
||||
width={800}
|
||||
height={600}
|
||||
style={{ aspectRatio: '800/600' }}
|
||||
/>
|
||||
|
||||
// 避免内容跳动
|
||||
<div style={{ minHeight: '200px' }}>
|
||||
{loading ? <Skeleton /> : <Content />}
|
||||
</div>
|
||||
```
|
||||
|
||||
### 3. 减少TBT
|
||||
|
||||
**问题**:TBT > 200ms
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 代码分割
|
||||
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
|
||||
loading: () => <Loading />,
|
||||
});
|
||||
|
||||
// 延迟加载非关键脚本
|
||||
useEffect(() => {
|
||||
import('heavy-library').then(module => {
|
||||
// 使用库
|
||||
});
|
||||
}, []);
|
||||
```
|
||||
|
||||
### 4. 优化FCP
|
||||
|
||||
**问题**:FCP > 1.8s
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 内联关键CSS
|
||||
<style dangerouslySetInnerHTML={{__html: criticalCSS}} />
|
||||
|
||||
// 预加载关键资源
|
||||
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossOrigin="anonymous" />
|
||||
|
||||
// 减少阻塞资源
|
||||
<script src="/script.js" defer></script>
|
||||
```
|
||||
|
||||
## 查看报告
|
||||
|
||||
### 本地报告
|
||||
|
||||
运行测试后,报告保存在`lighthouse-reports/`目录:
|
||||
|
||||
```bash
|
||||
# 打开最新报告
|
||||
open lighthouse-reports/lhci-*.html
|
||||
```
|
||||
|
||||
### 在线报告
|
||||
|
||||
如果使用临时公共存储,测试后会输出报告URL:
|
||||
|
||||
```
|
||||
✅ Report: https://storage.googleapis.com/lighthouse-infrastructure.appspot.com/reports/xxxxx.html
|
||||
```
|
||||
|
||||
### Lighthouse CI服务器
|
||||
|
||||
配置Lighthouse CI服务器后,可以查看历史趋势:
|
||||
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"upload": {
|
||||
"target": "lhci",
|
||||
"serverBaseUrl": "https://your-lhci-server.com",
|
||||
"token": "your-upload-token"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能监控仪表板
|
||||
|
||||
### 创建自定义仪表板
|
||||
|
||||
```typescript
|
||||
// scripts/performance-dashboard.ts
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
interface LighthouseResult {
|
||||
url: string;
|
||||
performance: number;
|
||||
accessibility: number;
|
||||
'best-practices': number;
|
||||
seo: number;
|
||||
lcp: number;
|
||||
fcp: number;
|
||||
cls: number;
|
||||
tbt: number;
|
||||
}
|
||||
|
||||
function generateDashboard(results: LighthouseResult[]) {
|
||||
const avgPerformance = results.reduce((sum, r) => sum + r.performance, 0) / results.length;
|
||||
const avgAccessibility = results.reduce((sum, r) => sum + r.accessibility, 0) / results.length;
|
||||
|
||||
console.log(`
|
||||
## 性能监控仪表板
|
||||
|
||||
### 平均分数
|
||||
- 性能: ${(avgPerformance * 100).toFixed(0)}/100
|
||||
- 可访问性: ${(avgAccessibility * 100).toFixed(0)}/100
|
||||
|
||||
### 各页面性能
|
||||
${results.map(r => `
|
||||
#### ${r.url}
|
||||
- 性能: ${(r.performance * 100).toFixed(0)}
|
||||
- LCP: ${r.lcp}ms
|
||||
- FCP: ${r.fcp}ms
|
||||
- CLS: ${r.cls}
|
||||
`).join('\n')}
|
||||
`);
|
||||
}
|
||||
|
||||
// 读取结果并生成仪表板
|
||||
const resultsPath = path.join(process.cwd(), 'lighthouse-reports', 'manifest.json');
|
||||
const results = JSON.parse(fs.readFileSync(resultsPath, 'utf-8'));
|
||||
generateDashboard(results);
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题1:服务器启动失败
|
||||
|
||||
**症状**:`Error: Server did not start in time`
|
||||
|
||||
**解决方案**:
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"startServerReadyPattern": "Local:",
|
||||
"startServerReadyTimeout": 60000
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题2:性能分数不达标
|
||||
|
||||
**症状**:`Assertion failed: categories:performance`
|
||||
|
||||
**解决方案**:
|
||||
1. 查看详细报告找出问题
|
||||
2. 根据优化建议进行改进
|
||||
3. 调整性能预算(临时)
|
||||
|
||||
```json
|
||||
{
|
||||
"assertions": {
|
||||
"categories:performance": ["warn", {"minScore": 0.8}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题3:测试超时
|
||||
|
||||
**症状**:`Navigation timeout of 30000 ms exceeded`
|
||||
|
||||
**解决方案**:
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"settings": {
|
||||
"maxWaitForLoad": 45000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### ✅ 推荐做法
|
||||
|
||||
1. **设置合理的性能预算**
|
||||
- 基于当前性能设置目标
|
||||
- 逐步提高标准
|
||||
|
||||
2. **定期运行测试**
|
||||
- 在CI/CD中自动运行
|
||||
- 每次部署前检查
|
||||
|
||||
3. **监控性能趋势**
|
||||
- 使用Lighthouse CI服务器
|
||||
- 跟踪性能变化
|
||||
|
||||
4. **优化关键页面**
|
||||
- 首页
|
||||
- 高流量页面
|
||||
- 转化页面
|
||||
|
||||
### ❌ 避免的做法
|
||||
|
||||
1. **不要设置过高的目标**
|
||||
```json
|
||||
// ❌ 不现实
|
||||
"categories:performance": ["error", {"minScore": 1.0}]
|
||||
|
||||
// ✅ 合理
|
||||
"categories:performance": ["error", {"minScore": 0.9}]
|
||||
```
|
||||
|
||||
2. **不要忽略移动端性能**
|
||||
```bash
|
||||
# 同时测试桌面和移动端
|
||||
npm run lighthouse:desktop
|
||||
npm run lighthouse:mobile
|
||||
```
|
||||
|
||||
3. **不要跳过性能测试**
|
||||
- 性能问题会影响用户体验
|
||||
- 早期发现问题更容易修复
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [Lighthouse CI官方文档](https://github.com/GoogleChrome/lighthouse-ci)
|
||||
- [Web Vitals](https://web.dev/vitals/)
|
||||
- [性能优化指南](https://web.dev/performance/)
|
||||
- [Lighthouse评分指南](https://web.dev/performance-scoring/)
|
||||
|
||||
## 总结
|
||||
|
||||
Lighthouse CI已完全集成到项目中,提供了:
|
||||
|
||||
✅ **自动化性能测试**
|
||||
✅ **性能预算监控**
|
||||
✅ **CI/CD集成**
|
||||
✅ **详细的性能报告**
|
||||
✅ **历史趋势跟踪**
|
||||
|
||||
通过合理使用Lighthouse CI,可以:
|
||||
- 保持良好的性能水平
|
||||
- 及时发现性能退化
|
||||
- 持续优化用户体验
|
||||
- 建立性能文化
|
||||
@@ -0,0 +1,466 @@
|
||||
# OpenAPI文档使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
OpenAPI(原名Swagger)是一个用于描述、生成、消费和可视化RESTful Web服务的规范。本项目已集成OpenAPI文档,提供交互式API文档界面。
|
||||
|
||||
## 访问文档
|
||||
|
||||
### 开发环境
|
||||
|
||||
启动开发服务器后,访问:
|
||||
|
||||
```
|
||||
http://localhost:3000/api-docs
|
||||
```
|
||||
|
||||
### 生产环境
|
||||
|
||||
部署后访问:
|
||||
|
||||
```
|
||||
https://your-domain.com/api-docs
|
||||
```
|
||||
|
||||
## 文档结构
|
||||
|
||||
### API端点
|
||||
|
||||
| 端点 | 方法 | 描述 | 认证 |
|
||||
|------|------|------|------|
|
||||
| `/api/health` | GET | 健康检查 | 无 |
|
||||
| `/api/admin/content` | GET | 获取内容列表 | 需要管理员权限 |
|
||||
| `/api/admin/content` | POST | 创建新内容 | 需要管理员权限 |
|
||||
|
||||
### 数据模型
|
||||
|
||||
#### Content(内容)
|
||||
|
||||
```typescript
|
||||
interface Content {
|
||||
id: number;
|
||||
type: 'news' | 'case' | 'product' | 'service';
|
||||
title: string;
|
||||
content: string;
|
||||
status: 'draft' | 'published' | 'archived';
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
```
|
||||
|
||||
#### User(用户)
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
role: 'admin' | 'editor' | 'viewer';
|
||||
}
|
||||
```
|
||||
|
||||
#### Config(配置)
|
||||
|
||||
```typescript
|
||||
interface Config {
|
||||
key: string;
|
||||
value: string;
|
||||
description: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 使用Swagger UI
|
||||
|
||||
### 浏览API
|
||||
|
||||
1. 访问 `/api-docs`
|
||||
2. 点击任意API端点展开详情
|
||||
3. 查看请求参数、响应格式和示例
|
||||
|
||||
### 测试API
|
||||
|
||||
#### 无需认证的API
|
||||
|
||||
1. 点击"Try it out"按钮
|
||||
2. 填写必要参数
|
||||
3. 点击"Execute"执行请求
|
||||
4. 查看响应结果
|
||||
|
||||
示例:健康检查API
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:3000/api/health" -H "accept: application/json"
|
||||
```
|
||||
|
||||
#### 需要认证的API
|
||||
|
||||
1. 先登录获取访问令牌
|
||||
2. 点击页面右上角的"Authorize"按钮
|
||||
3. 输入Bearer令牌:`Bearer your-access-token`
|
||||
4. 点击"Authorize"确认
|
||||
5. 现在可以测试需要认证的API
|
||||
|
||||
示例:获取内容列表
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:3000/api/admin/content?page=1&limit=20" \
|
||||
-H "accept: application/json" \
|
||||
-H "Authorization: Bearer your-access-token"
|
||||
```
|
||||
|
||||
## 为API添加文档
|
||||
|
||||
### 步骤1:添加JSDoc注释
|
||||
|
||||
在API路由文件中添加JSDoc注释:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* @openapi
|
||||
* /api/your-endpoint:
|
||||
* get:
|
||||
* tags:
|
||||
* - YourTag
|
||||
* summary: 简短描述
|
||||
* description: 详细描述
|
||||
* operationId: getYourData
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功响应
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* data:
|
||||
* type: object
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
// API实现
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤2:定义请求体
|
||||
|
||||
对于POST/PUT请求:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* @openapi
|
||||
* /api/your-endpoint:
|
||||
* post:
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - name
|
||||
* - email
|
||||
* properties:
|
||||
* name:
|
||||
* type: string
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
*/
|
||||
```
|
||||
|
||||
### 步骤3:引用共享Schema
|
||||
|
||||
使用`$ref`引用共享数据模型:
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* @openapi
|
||||
* /api/admin/content:
|
||||
* get:
|
||||
* responses:
|
||||
* 200:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Content'
|
||||
*/
|
||||
```
|
||||
|
||||
## OpenAPI规范文件
|
||||
|
||||
### 获取规范文件
|
||||
|
||||
访问以下端点获取原始OpenAPI规范:
|
||||
|
||||
```
|
||||
GET /api/docs
|
||||
```
|
||||
|
||||
### 使用规范文件
|
||||
|
||||
1. **导入到Postman**
|
||||
- 打开Postman
|
||||
- 点击"Import"
|
||||
- 选择"Link"
|
||||
- 输入:`http://localhost:3000/api/docs`
|
||||
- 点击"Import"
|
||||
|
||||
2. **生成客户端代码**
|
||||
- 使用OpenAPI Generator
|
||||
- 支持多种语言:TypeScript, Python, Java等
|
||||
|
||||
3. **API测试**
|
||||
- 导入到测试工具
|
||||
- 自动生成测试用例
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### ✅ 推荐做法
|
||||
|
||||
1. **完整的描述**
|
||||
- 提供清晰的summary和description
|
||||
- 说明参数的作用和限制
|
||||
- 提供示例值
|
||||
|
||||
2. **准确的类型定义**
|
||||
- 使用正确的数据类型
|
||||
- 标注必填字段
|
||||
- 定义枚举值
|
||||
|
||||
3. **完整的响应定义**
|
||||
- 定义所有可能的响应状态码
|
||||
- 提供错误响应格式
|
||||
- 包含示例数据
|
||||
|
||||
4. **合理的标签分组**
|
||||
- 按功能模块分组
|
||||
- 使用一致的命名
|
||||
- 避免过多标签
|
||||
|
||||
### ❌ 避免的做法
|
||||
|
||||
1. **不要省略错误响应**
|
||||
```typescript
|
||||
// ❌ 不好
|
||||
responses:
|
||||
* 200:
|
||||
* description: 成功
|
||||
|
||||
// ✅ 好
|
||||
responses:
|
||||
* 200:
|
||||
* description: 成功
|
||||
* 400:
|
||||
* description: 参数错误
|
||||
* 401:
|
||||
* description: 未授权
|
||||
* 500:
|
||||
* description: 服务器错误
|
||||
```
|
||||
|
||||
2. **不要使用模糊的描述**
|
||||
```typescript
|
||||
// ❌ 不好
|
||||
summary: 获取数据
|
||||
|
||||
// ✅ 好
|
||||
summary: 获取内容列表
|
||||
description: 管理员获取内容列表,支持分页、筛选和搜索
|
||||
```
|
||||
|
||||
3. **不要忽略认证要求**
|
||||
```typescript
|
||||
// ✅ 始终标注认证要求
|
||||
security:
|
||||
* - bearerAuth: []
|
||||
```
|
||||
|
||||
## 高级功能
|
||||
|
||||
### 添加示例
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* @openapi
|
||||
* /api/admin/content:
|
||||
* post:
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* examples:
|
||||
* newsExample:
|
||||
* summary: 新闻示例
|
||||
* value:
|
||||
* type: news
|
||||
* title: 新闻标题
|
||||
* content: 新闻内容
|
||||
*/
|
||||
```
|
||||
|
||||
### 添加标签描述
|
||||
|
||||
在`/api/docs/route.ts`中:
|
||||
|
||||
```typescript
|
||||
tags: [
|
||||
{
|
||||
name: 'Content',
|
||||
description: '内容管理相关接口',
|
||||
},
|
||||
{
|
||||
name: 'Admin',
|
||||
description: '管理员相关接口',
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
### 添加服务器配置
|
||||
|
||||
```typescript
|
||||
servers: [
|
||||
{
|
||||
url: 'http://localhost:3000',
|
||||
description: '开发服务器',
|
||||
},
|
||||
{
|
||||
url: 'https://api.novalon.cn',
|
||||
description: '生产服务器',
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
## CI/CD集成
|
||||
|
||||
### 验证OpenAPI规范
|
||||
|
||||
```bash
|
||||
# 安装验证工具
|
||||
npm install -g @redocly/cli
|
||||
|
||||
# 验证规范
|
||||
redocly lint http://localhost:3000/api/docs
|
||||
```
|
||||
|
||||
### 生成文档
|
||||
|
||||
```bash
|
||||
# 安装Redoc
|
||||
npm install -g redoc
|
||||
|
||||
# 生成静态HTML文档
|
||||
redocly build-docs http://localhost:3000/api/docs -o api-docs.html
|
||||
```
|
||||
|
||||
### GitHub Actions示例
|
||||
|
||||
```yaml
|
||||
name: API Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Start server
|
||||
run: npm run dev &
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Wait for server
|
||||
run: npx wait-on http://localhost:3000/api/docs
|
||||
|
||||
- name: Validate OpenAPI spec
|
||||
run: npx @redocly/cli lint http://localhost:3000/api/docs
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题1:文档页面无法加载
|
||||
|
||||
**症状**:访问`/api-docs`显示加载中或空白页
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查API端点是否正常
|
||||
curl http://localhost:3000/api/docs
|
||||
|
||||
# 检查浏览器控制台错误
|
||||
# 打开开发者工具查看Network和Console标签
|
||||
```
|
||||
|
||||
### 问题2:API不显示在文档中
|
||||
|
||||
**症状**:某些API端点未出现在文档中
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 检查JSDoc注释格式
|
||||
// 确保使用 @openapi 标签
|
||||
/**
|
||||
* @openapi // ← 必须是这个标签
|
||||
* /api/your-endpoint:
|
||||
* get:
|
||||
*/
|
||||
|
||||
// 检查apis路径配置
|
||||
apis: [
|
||||
'./src/app/api/**/route.ts', // ← 确保路径正确
|
||||
],
|
||||
```
|
||||
|
||||
### 问题3:认证失败
|
||||
|
||||
**症状**:使用Authorize按钮后仍然无法访问需要认证的API
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 确保令牌格式正确
|
||||
Bearer your-access-token # ← 注意Bearer前缀
|
||||
|
||||
# 检查令牌是否有效
|
||||
curl -H "Authorization: Bearer your-token" http://localhost:3000/api/admin/content
|
||||
```
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [OpenAPI规范](https://swagger.io/specification/)
|
||||
- [Swagger UI文档](https://swagger.io/tools/swagger-ui/)
|
||||
- [swagger-jsdoc文档](https://github.com/surnet/swagger-jsdoc)
|
||||
- [OpenAPI Generator](https://openapi-generator.tech/)
|
||||
- [Redoc文档](https://redocly.com/docs/redoc/)
|
||||
|
||||
## 总结
|
||||
|
||||
OpenAPI文档已完全集成到项目中,提供了:
|
||||
|
||||
✅ **交互式API文档**
|
||||
✅ **自动生成规范**
|
||||
✅ **在线测试功能**
|
||||
✅ **认证支持**
|
||||
✅ **多格式导出**
|
||||
|
||||
通过合理使用OpenAPI文档,可以:
|
||||
- 提升API可用性
|
||||
- 减少沟通成本
|
||||
- 自动化API测试
|
||||
- 生成客户端SDK
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,213 @@
|
||||
# 单元测试覆盖率改进计划
|
||||
|
||||
## 当前状态
|
||||
|
||||
**测试日期**: 2026-03-20
|
||||
|
||||
### 当前覆盖率
|
||||
|
||||
| 指标 | 当前值 | 目标值 | 差距 |
|
||||
|------|--------|--------|------|
|
||||
| **Lines** | 29.36% | 80% | -50.64% |
|
||||
| **Statements** | 29.5% | 80% | -50.5% |
|
||||
| **Functions** | 30.21% | 80% | -49.79% |
|
||||
| **Branches** | 22.66% | 80% | -57.34% |
|
||||
|
||||
### 当前阈值设置
|
||||
|
||||
```javascript
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 35,
|
||||
functions: 45,
|
||||
lines: 45,
|
||||
statements: 45,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 改进策略
|
||||
|
||||
### 阶段一:快速提升(当前 → 50%)
|
||||
|
||||
**时间**: 2周
|
||||
|
||||
**目标**: 将覆盖率从当前水平提升到50%
|
||||
|
||||
**重点区域**:
|
||||
1. **核心业务逻辑**
|
||||
- [ ] `src/app/(marketing)/contact/actions.ts` (当前: 0%)
|
||||
- [ ] `src/app/api/admin/content/route.ts` (当前: 0%)
|
||||
- [ ] `src/app/api/admin/users/route.ts` (当前: 0%)
|
||||
|
||||
2. **工具函数**
|
||||
- [ ] `src/lib/sanitize.ts`
|
||||
- [ ] `src/lib/csrf.ts`
|
||||
- [ ] `src/lib/constants.ts`
|
||||
|
||||
3. **Hooks**
|
||||
- [ ] `src/hooks/use-media-query.ts`
|
||||
- [ ] `src/hooks/use-scroll-reveal.ts`
|
||||
- [ ] `src/hooks/use-intersection-observer.ts`
|
||||
|
||||
**行动项**:
|
||||
- 为每个核心函数编写基础测试用例
|
||||
- 覆盖主要成功路径和常见错误场景
|
||||
- 使用Mock隔离外部依赖
|
||||
|
||||
### 阶段二:稳步推进(50% → 65%)
|
||||
|
||||
**时间**: 3周
|
||||
|
||||
**目标**: 将覆盖率从50%提升到65%
|
||||
|
||||
**重点区域**:
|
||||
1. **UI组件**
|
||||
- [ ] `src/components/ui/button.tsx`
|
||||
- [ ] `src/components/ui/input.tsx`
|
||||
- [ ] `src/components/ui/textarea.tsx`
|
||||
- [ ] `src/components/ui/toast.tsx`
|
||||
|
||||
2. **页面组件**
|
||||
- [ ] `src/app/(marketing)/about/page.tsx`
|
||||
- [ ] `src/app/(marketing)/products/page.tsx`
|
||||
- [ ] `src/app/(marketing)/services/page.tsx`
|
||||
|
||||
**行动项**:
|
||||
- 使用React Testing Library测试组件交互
|
||||
- 测试用户事件(点击、输入、提交)
|
||||
- 测试组件状态变化和副作用
|
||||
|
||||
### 阶段三:精细打磨(65% → 80%)
|
||||
|
||||
**时间**: 4周
|
||||
|
||||
**目标**: 将覆盖率从65%提升到80%
|
||||
|
||||
**重点区域**:
|
||||
1. **边界情况**
|
||||
- [ ] 错误处理逻辑
|
||||
- [ ] 空值/undefined处理
|
||||
- [ ] 异常输入验证
|
||||
|
||||
2. **复杂场景**
|
||||
- [ ] API集成测试
|
||||
- [ ] 数据库交互测试
|
||||
- [ ] 认证/授权流程
|
||||
|
||||
**行动项**:
|
||||
- 补充边界测试用例
|
||||
- 测试错误处理和异常流程
|
||||
- 使用MSW (Mock Service Worker)测试API调用
|
||||
- 使用Testcontainers测试数据库交互
|
||||
|
||||
## 执行计划
|
||||
|
||||
### 每周任务分配
|
||||
|
||||
#### Week 1-2: 核心业务逻辑
|
||||
- [ ] 联系表单Server Actions测试
|
||||
- [ ] 内容管理API测试
|
||||
- [ ] 用户管理API测试
|
||||
- [ ] 工具函数测试
|
||||
|
||||
#### Week 3-4: UI组件基础
|
||||
- [ ] 表单组件测试
|
||||
- [ ] 按钮组件测试
|
||||
- [ ] Toast组件测试
|
||||
- [ ] 基础页面组件测试
|
||||
|
||||
#### Week 5-7: 页面组件
|
||||
- [ ] 营销页面测试
|
||||
- [ ] 产品页面测试
|
||||
- [ ] 服务页面测试
|
||||
- [ ] 案例页面测试
|
||||
|
||||
#### Week 8-9: 边界情况
|
||||
- [ ] 错误处理测试
|
||||
- [ ] 边界值测试
|
||||
- [ ] 异常流程测试
|
||||
|
||||
## 工具和资源
|
||||
|
||||
### 测试工具
|
||||
- **Jest**: 单元测试框架
|
||||
- **React Testing Library**: React组件测试
|
||||
- **MSW**: API Mock
|
||||
- **Testcontainers**: 数据库集成测试
|
||||
|
||||
### 覆盖率报告
|
||||
```bash
|
||||
# 生成覆盖率报告
|
||||
npm run test:coverage
|
||||
|
||||
# 查看HTML报告
|
||||
open coverage/lcov-report/index.html
|
||||
```
|
||||
|
||||
### CI集成
|
||||
```yaml
|
||||
# GitHub Actions示例
|
||||
- name: Run tests with coverage
|
||||
run: npm run test:coverage:check
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage/coverage-final.json
|
||||
```
|
||||
|
||||
## 验收标准
|
||||
|
||||
### 阶段一完成标准
|
||||
- [ ] 核心业务逻辑覆盖率 ≥ 70%
|
||||
- [ ] 工具函数覆盖率 ≥ 80%
|
||||
- [ ] Hooks覆盖率 ≥ 60%
|
||||
- [ ] 总体覆盖率 ≥ 50%
|
||||
|
||||
### 阶段二完成标准
|
||||
- [ ] UI组件覆盖率 ≥ 60%
|
||||
- [ ] 页面组件覆盖率 ≥ 50%
|
||||
- [ ] 总体覆盖率 ≥ 65%
|
||||
|
||||
### 阶段三完成标准
|
||||
- [ ] 所有模块覆盖率 ≥ 75%
|
||||
- [ ] 总体覆盖率 ≥ 80%
|
||||
- [ ] CI中强制执行覆盖率阈值
|
||||
|
||||
## 持续改进
|
||||
|
||||
### 定期审查
|
||||
- 每周审查覆盖率报告
|
||||
- 识别低覆盖率模块
|
||||
- 优先测试高影响区域
|
||||
|
||||
### 新代码要求
|
||||
- 所有新代码必须包含单元测试
|
||||
- 新代码覆盖率要求 ≥ 80%
|
||||
- PR必须通过覆盖率检查
|
||||
|
||||
### 重构支持
|
||||
- 重构前确保有足够的测试覆盖
|
||||
- 重构后验证测试仍然通过
|
||||
- 利用测试作为重构的安全网
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **不要为了覆盖率而写测试**
|
||||
- 测试应该验证行为,而不是代码行
|
||||
- 有意义的测试比高覆盖率更重要
|
||||
|
||||
2. **关注关键路径**
|
||||
- 优先测试核心业务逻辑
|
||||
- 确保关键功能有充分的测试覆盖
|
||||
|
||||
3. **保持测试质量**
|
||||
- 测试代码也需要维护
|
||||
- 定期清理和重构测试代码
|
||||
- 避免脆弱的测试
|
||||
|
||||
4. **平衡投入产出**
|
||||
- 80%覆盖率是目标,不是硬性要求
|
||||
- 某些代码可能不需要测试(如简单的getter/setter)
|
||||
- 根据风险和重要性分配测试资源
|
||||
Reference in New Issue
Block a user