From bf6917f5e809bbfec8ae405982b78586af3d7f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Tue, 23 Sep 2025 17:54:22 +0800 Subject: [PATCH 1/5] =?UTF-8?q?H5=E9=AB=98=E5=BA=A6=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pages/work-flow/H5MediaViewer.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/components/pages/work-flow/H5MediaViewer.tsx b/components/pages/work-flow/H5MediaViewer.tsx index f1abdd8..8a5237f 100644 --- a/components/pages/work-flow/H5MediaViewer.tsx +++ b/components/pages/work-flow/H5MediaViewer.tsx @@ -164,7 +164,9 @@ export function H5MediaViewer({ // 渲染视频 slide const renderVideoSlides = () => ( -
+
) : (
{status === 0 && ( Generating... @@ -248,7 +250,9 @@ export function H5MediaViewer({ // 渲染图片 slide const renderImageSlides = () => ( -
+
Date: Tue, 23 Sep 2025 19:45:34 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E8=B0=B7=E6=AD=8C=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=88=90=E5=8A=9F=E5=90=8E=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/users/oauth/callback/page.tsx | 35 +++++-- docs/oauth-callback-fix-summary.md | 153 +++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 docs/oauth-callback-fix-summary.md diff --git a/app/users/oauth/callback/page.tsx b/app/users/oauth/callback/page.tsx index 7b835cd..7989731 100644 --- a/app/users/oauth/callback/page.tsx +++ b/app/users/oauth/callback/page.tsx @@ -5,9 +5,10 @@ import { useRouter, useSearchParams } from "next/navigation"; import { CheckCircle, XCircle, Loader2, AlertTriangle } from "lucide-react"; import type { OAuthCallbackParams } from "@/app/types/google-oauth"; -// 根据接口文档定义响应类型 +// 根据后端实际返回格式定义响应类型 interface GoogleOAuthResponse { - success: boolean; + code: number; + message: string; data: { token: string; user: { @@ -21,7 +22,7 @@ interface GoogleOAuthResponse { }; message: string; }; - message?: string; + successful: boolean; } export default function OAuthCallback() { @@ -117,7 +118,13 @@ export default function OAuthCallback() { console.log('Python OAuth接口响应状态:', response.status); const result: GoogleOAuthResponse = await response.json(); - if (!response.ok || !result.success) { + console.log('🔍 后端返回的完整数据:', result); + + // 根据后端实际返回格式判断成功状态 + // 后端返回: { code: 0, successful: true } 表示成功 + const isSuccess = response.ok && result.successful && result.code === 0; + + if (!isSuccess) { console.error('Python OAuth接口处理失败:', result); // 处理常见错误码 @@ -129,21 +136,25 @@ export default function OAuthCallback() { throw new Error('Authentication service error. Please try again later.'); } - throw new Error(result.message || 'Google OAuth failed'); + // 根据后端返回格式处理错误 + const errorMessage = result.message || `OAuth failed (code: ${result.code})`; + throw new Error(errorMessage); } - console.log('Google OAuth成功:', { + console.log('🎉 Google OAuth成功:', { userId: result.data?.user?.userId, email: result.data?.user?.email, - isNewUser: result.data?.user?.isNewUser + isNewUser: result.data?.user?.isNewUser, + code: result.code, + successful: result.successful }); // 处理成功结果 - console.log('Google登录成功:', result); + console.log('✅ Google登录成功,完整响应:', result); setStatus("success"); setMessage(result.data.message || "Login successful! Redirecting to dashboard..."); - // 根据接口文档的响应格式保存用户信息 + // 根据后端实际响应格式保存用户信息 const { token, user } = result.data; // 保存用户信息到localStorage @@ -157,14 +168,20 @@ export default function OAuthCallback() { isNewUser: user.isNewUser }; + console.log('💾 保存用户数据到 localStorage:', userData); localStorage.setItem('currentUser', JSON.stringify(userData)); + if (token) { + console.log('🔑 保存 token 到 localStorage'); localStorage.setItem('token', token); } + console.log('⏰ 准备在2秒后跳转到 /movies'); + // 2秒后跳转到主页 setTimeout(() => { const returnUrl = '/movies'; + console.log('🚀 开始跳转到:', returnUrl); window.location.href = returnUrl; }, 2000); diff --git a/docs/oauth-callback-fix-summary.md b/docs/oauth-callback-fix-summary.md new file mode 100644 index 0000000..e1b734d --- /dev/null +++ b/docs/oauth-callback-fix-summary.md @@ -0,0 +1,153 @@ +# OAuth 回调页面跳转问题修复总结 + +## 🚨 问题描述 + +用户反馈:调用后端 `api/oauth/google` 接口返回成功,但前端成功后没有跳转到 `/movies` 页面。 + +## 🔍 问题根因分析 + +### 后端实际返回格式: +```json +{ + "code": 0, + "message": "ok", + "data": { + "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", + "user": { + "userId": "0a005fe7181e40baafdd80ce3c2ce98d", + "userName": "Zixin Zhou", + "name": "Zixin Zhou", + "email": "codecaesar2020@gmail.com", + "authType": "GOOGLE", + "avatar": "https://lh3.googleusercontent.com/a/ACg8ocLS9E_7EIntgaxYFuczaU3laxooRltg2JIbnzc6osnANS8beL8=s96-c", + "isNewUser": false + }, + "message": "Google OAuth successful" + }, + "successful": true +} +``` + +### 前端原来期望的格式: +```typescript +interface GoogleOAuthResponse { + success: boolean; // ❌ 后端用的是 successful + data: { ... }; + message?: string; +} +``` + +### 关键问题: +1. **字段名不匹配**:后端用 `successful: true`,前端检查 `success` +2. **状态码处理**:后端用 `code: 0` 表示成功,前端没有处理 +3. **成功判断逻辑错误**:导致成功的响应被当作失败处理 + +## 🔧 修复方案 + +### 1. 更新响应类型定义 + +**修改前**: +```typescript +interface GoogleOAuthResponse { + success: boolean; + data: { ... }; + message?: string; +} +``` + +**修改后**: +```typescript +interface GoogleOAuthResponse { + code: number; + message: string; + data: { + token: string; + user: { ... }; + message: string; + }; + successful: boolean; +} +``` + +### 2. 修复成功判断逻辑 + +**修改前**: +```typescript +if (!response.ok || !result.success) { + // 错误处理 +} +``` + +**修改后**: +```typescript +// 根据后端实际返回格式判断成功状态 +// 后端返回: { code: 0, successful: true } 表示成功 +const isSuccess = response.ok && result.successful && result.code === 0; + +if (!isSuccess) { + // 错误处理 +} +``` + +### 3. 增强调试日志 + +添加了详细的调试日志来跟踪整个流程: +- 后端返回的完整数据 +- 成功状态判断 +- localStorage 数据保存 +- 跳转准备和执行 + +## 📋 修改的文件 + +- **`app/users/oauth/callback/page.tsx`** + - 更新了 `GoogleOAuthResponse` 接口定义 + - 修复了成功状态判断逻辑 + - 增加了详细的调试日志 + - 保持了2秒延迟跳转到 `/movies` 的逻辑 + +## 🧪 测试验证 + +修复后的流程应该是: + +1. **接收后端响应**: + ``` + 🔍 后端返回的完整数据: { code: 0, successful: true, ... } + ``` + +2. **成功状态判断**: + ``` + 🎉 Google OAuth成功: { userId: "...", email: "...", code: 0, successful: true } + ``` + +3. **数据保存**: + ``` + 💾 保存用户数据到 localStorage: { userId: "...", userName: "...", ... } + 🔑 保存 token 到 localStorage + ``` + +4. **跳转准备**: + ``` + ⏰ 准备在2秒后跳转到 /movies + 🚀 开始跳转到: /movies + ``` + +## 🎯 预期结果 + +修复后,当后端返回成功响应时: +1. 前端能正确识别成功状态 +2. 用户数据和 token 正确保存到 localStorage +3. 2秒后自动跳转到 `/movies` 页面 +4. 控制台显示详细的调试信息 + +## 🔄 回滚方案 + +如果修复后出现问题,可以: +1. 恢复原来的接口定义 +2. 联系后端开发者统一响应格式 +3. 或者在前端做兼容性处理 + +## 📝 注意事项 + +- 这次修复是基于后端实际返回的数据格式 +- 如果后端响应格式发生变化,需要相应更新前端代码 +- 建议前后端团队统一 API 响应格式规范 From 6d3adc9baec1bbf16e499cd4816578a8bbbdc429 Mon Sep 17 00:00:00 2001 From: qikongjian Date: Tue, 23 Sep 2025 19:46:17 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=89=AA=E8=BE=91=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E5=90=8E=E4=B8=8D=E8=B5=B0=E4=B8=8B=E8=BD=BD,=E8=AE=A9?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=89=8B=E5=8A=A8=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/workflow-auto-download-disabled.md | 123 ++++++++++++++++++++++++ utils/export-service.ts | 20 ++-- 2 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 docs/workflow-auto-download-disabled.md diff --git a/docs/workflow-auto-download-disabled.md b/docs/workflow-auto-download-disabled.md new file mode 100644 index 0000000..ad12ac9 --- /dev/null +++ b/docs/workflow-auto-download-disabled.md @@ -0,0 +1,123 @@ +# Video-Flow 自动下载功能禁用说明 + +## 📋 修改概述 + +根据需求,已将 Video-Flow 工作流中的自动下载视频功能暂时禁用,用户需要手动下载生成的视频。 + +## 🔍 工作流程梳理 + +### 完整的 Work-Flow 自动调用剪辑接口流程 + +```mermaid +graph TB + A[用户在 work-flow 页面] --> B{视频片段生成完成?} + B -->|是| C[显示 AI 剪辑按钮] + B -->|否| D[等待视频生成] + + C --> E[用户点击 AI 剪辑按钮] + E --> F[ai-editing-adapter.ts 处理] + F --> G[调用 /api/export/ai-clips 接口] + G --> H[执行 AI 剪辑处理] + H --> I[生成最终视频] + I --> J[返回视频 URL] + J --> K[显示手动下载按钮] + + style K fill:#90EE90 + style I fill:#FFE4B5 +``` + +### 涉及的核心文件 + +1. **`components/pages/work-flow.tsx`** - 主工作流页面 +2. **`components/pages/work-flow/ai-editing-button.tsx`** - AI 剪辑按钮组件 +3. **`components/pages/work-flow/ai-editing-adapter.ts`** - AI 剪辑适配器 +4. **`utils/export-service.ts`** - 导出服务(已禁用自动下载) +5. **`app/api/export/ai-clips/route.ts`** - 剪辑接口 +6. **`app/api/export/download/[exportId]/route.ts`** - 下载接口 + +## 🚫 已禁用的自动下载功能 + +### 修改位置 1: `utils/export-service.ts` (第685-701行) + +**修改前**: +```typescript +// 如果SSE中直接有完整结果,直接处理 +if (result?.download_url || result?.video_url) { + const downloadUrl = result.download_url || result.video_url; + console.log('📥 直接从SSE结果下载视频:', downloadUrl); + await downloadVideo(downloadUrl); // 🚫 自动下载 +} +``` + +**修改后**: +```typescript +// 如果SSE中直接有完整结果,直接处理 +if (result?.download_url || result?.video_url) { + const downloadUrl = result.download_url || result.video_url; + console.log('📥 直接从SSE结果获取视频URL:', downloadUrl); + + // 🚫 暂时不支持自动下载 - 已注释 + // await downloadVideo(downloadUrl); +} +``` + +### 修改位置 2: `utils/export-service.ts` (第726-736行) + +**修改前**: +```typescript +// 自动下载视频 +if (finalExportResult.video_url) { + console.log('📥 开始下载视频:', finalExportResult.video_url); + await downloadVideo(finalExportResult.video_url); // 🚫 自动下载 + console.log('✅ 视频下载完成'); +} +``` + +**修改后**: +```typescript +// 🚫 暂时不支持自动下载 - 已注释 +if (finalExportResult.video_url) { + console.log('📥 视频导出完成,URL:', finalExportResult.video_url); + // await downloadVideo(finalExportResult.video_url); + console.log('✅ 视频导出完成,用户可手动下载'); +} +``` + +## ✅ 保留的手动下载功能 + +以下手动下载功能仍然保留: + +### 1. AI 剪辑完成后的下载按钮 +- **文件**: `components/pages/work-flow/ai-editing-button.tsx` +- **位置**: 第284-298行 +- **功能**: AI 剪辑完成后显示下载按钮,用户可手动点击下载 + +### 2. 媒体查看器中的下载按钮 +- **文件**: `components/pages/work-flow/media-viewer.tsx` +- **功能**: 用户可手动下载单个视频或所有视频 + +### 3. 项目列表中的下载按钮 +- **文件**: `components/pages/create-to-video2.tsx` +- **功能**: 用户可在项目列表中手动下载最终视频 + +## 🔄 如何重新启用自动下载 + +如果将来需要重新启用自动下载功能,只需: + +1. 在 `utils/export-service.ts` 中取消注释 `await downloadVideo(downloadUrl)` 行 +2. 将相关的 console.log 消息改回原来的描述 +3. 测试确保自动下载功能正常工作 + +## 📝 注意事项 + +- 自动下载功能的禁用不影响视频的生成和处理 +- 用户仍然可以通过手动点击下载按钮来下载视频 +- 所有的下载接口和相关功能都保持完整,只是不会自动触发 +- 这个修改是临时的,可以随时恢复 + +## 🧪 测试建议 + +1. 测试 AI 剪辑流程是否正常工作 +2. 确认剪辑完成后不会自动下载 +3. 验证手动下载按钮是否正常显示和工作 +4. 检查控制台日志确认修改生效 diff --git a/utils/export-service.ts b/utils/export-service.ts index c537cc6..ed9b734 100644 --- a/utils/export-service.ts +++ b/utils/export-service.ts @@ -685,13 +685,15 @@ export class VideoExportService { // 如果SSE中直接有完整结果,直接处理 if (result?.download_url || result?.video_url) { const downloadUrl = result.download_url || result.video_url; - console.log('📥 直接从SSE结果下载视频:', downloadUrl); - await downloadVideo(downloadUrl); - + console.log('📥 直接从SSE结果获取视频URL:', downloadUrl); + + // 🚫 暂时不支持自动下载 - 已注释 + // await downloadVideo(downloadUrl); + // notification.success({ // message: '视频下载完成!', - // description: result?.file_size - // ? `文件大小: ${(result.file_size / 1024 / 1024).toFixed(2)}MB` + // description: result?.file_size + // ? `文件大小: ${(result.file_size / 1024 / 1024).toFixed(2)}MB` // : '视频已成功下载到本地', // placement: 'topRight', // duration: 8 @@ -721,16 +723,16 @@ export class VideoExportService { }); */ - // 自动下载视频 + // 🚫 暂时不支持自动下载 - 已注释 if (finalExportResult.video_url) { - console.log('📥 开始下载视频:', finalExportResult.video_url); + console.log('📥 视频导出完成,URL:', finalExportResult.video_url); console.log('📋 视频文件信息:', { url: finalExportResult.video_url, file_size: finalExportResult.file_size, quality_mode: finalExportResult.quality_mode }); - await downloadVideo(finalExportResult.video_url); - console.log('✅ 视频下载完成'); + // await downloadVideo(finalExportResult.video_url); + console.log('✅ 视频导出完成,用户可手动下载'); } // 清除缓存的请求数据 From b011a784c3fba9f7b59cd118ffe5d0f57bdc379f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Tue, 23 Sep 2025 19:53:02 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E8=B0=83=E6=95=B4H5=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E9=A1=B6=E9=83=A8=E5=AF=BC=E8=88=AA=E6=A0=8F=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/layout/top-bar.tsx | 13 +++++++++++-- components/pages/home-page2.tsx | 19 +++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/components/layout/top-bar.tsx b/components/layout/top-bar.tsx index 850c9c6..c83c405 100644 --- a/components/layout/top-bar.tsx +++ b/components/layout/top-bar.tsx @@ -261,8 +261,17 @@ export function TopBar({ collapsed, isDesktop=true }: { collapsed: boolean, isDe left: (pathname === "/" || !isDesktop) ? "0" : (collapsed ? "2.5rem" : "16rem") }} > -
-
+
+
+ {pathname === "/" && ( + + )}
router.push("/")} diff --git a/components/pages/home-page2.tsx b/components/pages/home-page2.tsx index 209db41..0eb4bba 100644 --- a/components/pages/home-page2.tsx +++ b/components/pages/home-page2.tsx @@ -217,6 +217,11 @@ export function HomePage2() { const NavBar = () => { if (homeTabs.length === 0) return null; + useEffect(() => { + const handler = () => setMenuOpen((v) => !v); + window.addEventListener('home-menu-toggle' as any, handler as any); + return () => window.removeEventListener('home-menu-toggle' as any, handler as any); + }, []); return (
@@ -234,14 +239,8 @@ export function HomePage2() { ))}
- {/* 移动端开关 */} - + {/* 移动端开关移至 TopBar,保留占位对齐 */} +
{/* 移动端下拉(仅三个项) */} {menuOpen && ( @@ -1396,9 +1395,9 @@ function HomeModule5() { {/* 主要价格卡片 */}
Date: Tue, 23 Sep 2025 20:04:08 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E7=99=BB=E5=BD=95=20Sign=20in=20with=20Goo?= =?UTF-8?q?gle=20=E6=B3=A8=E5=86=8C=20Sign=20up=20with=20Google?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/signup/page.tsx | 4 +++- components/pages/login.tsx | 2 ++ components/ui/google-login-button.tsx | 12 ++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/signup/page.tsx b/app/signup/page.tsx index 5b21eb2..33a17e1 100644 --- a/app/signup/page.tsx +++ b/app/signup/page.tsx @@ -525,6 +525,8 @@ export default function SignupPage() { variant="outline" size="md" className="w-full" + label="Sign up with Google" + loadingLabel="Signing up..." /> )} @@ -578,7 +580,7 @@ export default function SignupPage() {

Please verify your email to activate your account

- We have sent an activation email to {email || "your email"}; if you don't receive it, please check your spam folder or try again later + We have sent an activation email to {email || "your email"}; if you don't receive it, please check your spam folder or try again later

diff --git a/components/pages/login.tsx b/components/pages/login.tsx index 8defed2..1103b0a 100644 --- a/components/pages/login.tsx +++ b/components/pages/login.tsx @@ -297,6 +297,8 @@ export default function Login() { variant="outline" size="md" className="w-full" + label="Sign in with Google" + loadingLabel="Signing in..." /> )} diff --git a/components/ui/google-login-button.tsx b/components/ui/google-login-button.tsx index df83dbc..1f4e085 100644 --- a/components/ui/google-login-button.tsx +++ b/components/ui/google-login-button.tsx @@ -11,6 +11,10 @@ interface GoogleLoginButtonProps { className?: string; size?: "sm" | "md" | "lg"; variant?: "default" | "outline"; + /** Button label when not loading */ + label?: string; + /** Button label when loading */ + loadingLabel?: string; } // Google logo SVG component matching the exact reference @@ -83,6 +87,8 @@ export const GoogleLoginButton = React.forwardRef< className, size = "md", variant = "default", + label = "Google Login", + loadingLabel = "Signing in...", ...props }, ref @@ -147,7 +153,7 @@ export const GoogleLoginButton = React.forwardRef< // Custom className className )} - aria-label={loading ? "Signing in with Google" : "Google Login"} + aria-label={loading ? loadingLabel : label} aria-disabled={isDisabled} role="button" tabIndex={isDisabled ? -1 : 0} @@ -158,9 +164,7 @@ export const GoogleLoginButton = React.forwardRef< ) : ( )} - - {loading ? "Signing in..." : "Google Login"} - + {loading ? loadingLabel : label} ); }