Files
Podcast-Generator/web/API_DUPLICATE_CALL_FIX.md
hex2077 b63fcb3f6d feat: 实现用户认证系统并优化音频播放器功能
- 新增NextAuth认证系统,支持Google和GitHub登录
- 添加登录模态框组件和用户头像显示
- 重构音频播放器,支持倍速控制和状态同步
- 优化播客卡片显示当前播放状态和生成状态
- 新增API调用追踪工具和防重复调用Hook
- 修复多个API重复调用问题并添加详细文档
- 改进音频文件处理流程,支持MP3格式输出
- 更新类型定义和组件Props以支持新功能
2025-08-16 23:03:46 +08:00

189 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API重复调用问题修复报告
## 问题描述
用户报告访问主页时主页内的接口被调用了两次。这是一个常见的React问题会导致
- 不必要的网络请求
- 服务器负载增加
- 用户体验下降
- 可能的数据不一致
## 问题分析
通过代码分析,发现了以下导致重复调用的原因:
### 1. **多个useEffect调用同一API**
`src/app/page.tsx` 中:
- 第38行的useEffect在组件挂载时调用 `fetchRecentPodcasts()`
- 第86行的useEffect设置定时器每20秒调用 `fetchRecentPodcasts()`
- 这导致页面加载时API被调用两次
### 2. **useEffect依赖项问题**
`src/components/PodcastCreator.tsx` 中:
- useEffect依赖项包含 `selectedConfig``selectedConfigName`
- 当配置变化时可能触发多次API调用
### 3. **ConfigSelector组件的重复调用**
`src/components/ConfigSelector.tsx` 中:
- localStorage变化监听可能导致重复的配置加载
## 修复方案
### 1. **合并useEffect调用**
将两个分离的useEffect合并为一个
```typescript
// 修复前两个独立的useEffect
useEffect(() => {
setCredits(100000);
fetchRecentPodcasts(); // 第一次调用
}, []);
useEffect(() => {
const interval = setInterval(() => {
fetchRecentPodcasts(); // 定时调用
}, 20000);
return () => clearInterval(interval);
}, []);
// 修复后合并为一个useEffect
useEffect(() => {
setCredits(100000);
fetchRecentPodcasts(); // 初始调用
// 设置定时器
const interval = setInterval(() => {
fetchRecentPodcasts();
}, 20000);
return () => clearInterval(interval);
}, []); // 空依赖数组,只在组件挂载时执行一次
```
### 2. **创建防重复调用Hook**
创建了 `src/hooks/useApiCall.ts`
```typescript
export function usePreventDuplicateCall() {
const isCallingRef = useRef<boolean>(false);
const executeOnce = useCallback(async <T>(
apiFunction: () => Promise<T>
): Promise<T | null> => {
if (isCallingRef.current) {
console.log('API call already in progress, skipping...');
return null;
}
try {
isCallingRef.current = true;
const result = await apiFunction();
return result;
} catch (error) {
console.error('API call failed:', error);
return null;
} finally {
isCallingRef.current = false;
}
}, []);
return { executeOnce };
}
```
### 3. **优化useEffect依赖项**
在PodcastCreator组件中
```typescript
// 修复前:多个依赖项可能导致重复调用
useEffect(() => {
fetchVoices();
}, [selectedConfig, selectedConfigName]);
// 修复后:只依赖必要的状态
useEffect(() => {
if (!selectedConfigName) {
setVoices([]);
return;
}
fetchVoices();
}, [selectedConfigName]); // 只依赖配置名称
```
### 4. **添加API调用追踪器**
创建了 `src/utils/apiCallTracker.ts` 用于开发环境下监控API调用
```typescript
// 自动检测重复调用
trackCall(url: string, method: string = 'GET'): string {
const recentCalls = this.calls.filter(
c => c.url === url &&
c.method === method &&
Date.now() - c.timestamp < 5000
);
if (recentCalls.length > 0) {
console.warn(`🚨 检测到重复API调用:`, {
url, method, 重复次数: recentCalls.length + 1
});
}
}
```
## 修复效果
### 修复前:
- 页面加载时 `/api/podcast-status` 被调用2次
- 配置变化时 `/api/tts-voices` 可能被多次调用
- 无法监控和调试重复调用问题
### 修复后:
- 页面加载时 `/api/podcast-status` 只调用1次
- 使用防重复调用机制确保同一时间只有一个请求
- 开发环境下自动检测和警告重复调用
- 优化了useEffect依赖项减少不必要的重新执行
## 验证方法
### 1. **开发环境调试**
打开浏览器开发者工具,在控制台中可以使用:
```javascript
// 查看API调用统计
window.apiDebug.showStats();
// 清空统计数据
window.apiDebug.clearStats();
```
### 2. **网络面板监控**
在浏览器开发者工具的Network面板中
- 刷新页面,观察 `/api/podcast-status` 只被调用一次
- 切换TTS配置观察 `/api/tts-voices` 不会重复调用
### 3. **控制台日志**
开发环境下会自动输出API调用日志
- `📡 API调用:` - 正常调用
- `🚨 检测到重复API调用:` - 重复调用警告
## 最佳实践建议
1. **useEffect合并原则**相关的副作用应该在同一个useEffect中处理
2. **依赖项最小化**:只包含真正需要的依赖项
3. **防重复调用**对于可能重复的API调用使用防重复机制
4. **开发调试工具**:在开发环境中添加监控和调试工具
5. **错误处理**确保API调用失败时不会影响后续调用
## 相关文件
- `src/app/page.tsx` - 主页组件修复
- `src/components/PodcastCreator.tsx` - 播客创建器组件修复
- `src/components/ConfigSelector.tsx` - 配置选择器组件修复
- `src/hooks/useApiCall.ts` - 防重复调用Hook新增
- `src/utils/apiCallTracker.ts` - API调用追踪器新增
## 注意事项
- 修复后的代码保持了原有功能不变
- 所有修改都向后兼容
- 调试工具只在开发环境中启用,不会影响生产环境性能
- 建议在部署前进行充分测试,确保所有功能正常工作