feat: 添加管理后台页面和功能,优化测试和性能配置

refactor: 重构页面导航和滚动逻辑,提升用户体验

test: 更新测试配置和用例,增加覆盖率和稳定性

perf: 优化性能指标和阈值,适应开发环境需求

ci: 添加Lighthouse CI工作流,集成性能测试

docs: 更新API文档和健康检查端点

fix: 修复登录页面和表单提交问题

style: 调整响应式布局和可访问性改进

chore: 更新依赖项和脚本配置
This commit is contained in:
张翔
2026-03-24 10:11:30 +08:00
parent 08978d38c8
commit f5dec95a83
85 changed files with 12331 additions and 1408 deletions
+445
View File
@@ -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报告,可以:
- 快速定位测试失败原因
- 追踪测试稳定性
- 分析测试性能
- 提升测试质量
+512
View File
@@ -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的长期健康
+513
View File
@@ -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,可以:
- 保持良好的性能水平
- 及时发现性能退化
- 持续优化用户体验
- 建立性能文化
+466
View File
@@ -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标签
```
### 问题2API不显示在文档中
**症状**:某些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
+213
View File
@@ -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
- 根据风险和重要性分配测试资源