diff --git a/.env.production b/.env.production index 72f8672..5adf7c6 100644 --- a/.env.production +++ b/.env.production @@ -1,5 +1,4 @@ -# 临时使用旧域名配置,等待后端更新 # 测试 # NEXT_PUBLIC_JAVA_URL = https://auth.test.movieflow.ai # NEXT_PUBLIC_BASE_URL = https://77.smartvideo.py.qikongjian.com diff --git a/app/service/Interaction/templateStoryService.ts b/app/service/Interaction/templateStoryService.ts index 7a35770..03bc35c 100644 --- a/app/service/Interaction/templateStoryService.ts +++ b/app/service/Interaction/templateStoryService.ts @@ -94,7 +94,6 @@ export const useTemplateStoryServiceHook = (): UseTemplateStoryService => { setTemplateStoryList(templates); setSelectedTemplate(templates[0]); - console.log(selectedTemplate); } catch (err) { console.error("获取模板列表失败:", err); } finally { diff --git a/app/users/oauth/callback/page.tsx b/app/users/oauth/callback/page.tsx index 40b9313..7b835cd 100644 --- a/app/users/oauth/callback/page.tsx +++ b/app/users/oauth/callback/page.tsx @@ -3,8 +3,26 @@ import React, { useEffect, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { CheckCircle, XCircle, Loader2, AlertTriangle } from "lucide-react"; -import { loginWithGoogleToken } from "@/lib/auth"; -import type { OAuthCallbackParams, OAuthState } from "@/app/types/google-oauth"; +import type { OAuthCallbackParams } from "@/app/types/google-oauth"; + +// 根据接口文档定义响应类型 +interface GoogleOAuthResponse { + success: boolean; + data: { + token: string; + user: { + userId: string; + userName: string; + name: string; + email: string; + authType: "GOOGLE"; + avatar: string; + isNewUser: boolean; + }; + message: string; + }; + message?: string; +} export default function OAuthCallback() { const router = useRouter(); @@ -78,98 +96,70 @@ export default function OAuthCallback() { console.log('State数据:', stateData); console.log('最终使用的邀请码:', finalInviteCode); - // 直接处理 OAuth 回调(两步流程:Java验证 + Python注册) - console.log('开始直接处理 OAuth 回调,无需经过 API 路由'); - - // 第一步:调用Java验证接口(只验证不创建用户) - const javaBaseUrl = 'https://auth.test.movieflow.ai'; - console.log('🔧 调用 Java 验证接口:', javaBaseUrl); + // 根据 jiekou.md 文档调用统一的 Python OAuth 接口 + // 使用 NEXT_PUBLIC_BASE_URL 配置,默认为 https://77.smartvideo.py.qikongjian.com + const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://77.smartvideo.py.qikongjian.com'; + console.log('🔧 调用 Python OAuth 接口:', baseUrl); - const verifyResponse = await fetch(`${javaBaseUrl}/api/auth/google/callback`, { + const response = await fetch(`${baseUrl}/api/oauth/google`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ - code: params.code, // Google authorization code - state: params.state, // state参数 - inviteCode: finalInviteCode, // 邀请码 - skipUserCreation: true // 🔑 关键:只验证不创建用户 + code: params.code, + state: params.state, + invite_code: finalInviteCode || null }) }); - console.log('Java验证接口响应状态:', verifyResponse.status); - const verifyResult = await verifyResponse.json(); + console.log('Python OAuth接口响应状态:', response.status); + const result: GoogleOAuthResponse = await response.json(); - if (!verifyResponse.ok || !verifyResult.success) { - console.error('Java验证接口处理失败:', verifyResult); - throw new Error(verifyResult.message || 'Google token verification failed'); + if (!response.ok || !result.success) { + console.error('Python OAuth接口处理失败:', result); + + // 处理常见错误码 + if (result.message?.includes('GOOGLE_TOKEN_EXCHANGE_FAILED')) { + throw new Error('Google authorization failed. Please try again.'); + } else if (result.message?.includes('INVALID_ID_TOKEN')) { + throw new Error('Invalid Google token. Please try again.'); + } else if (result.message?.includes('UPSTREAM_AUTH_ERROR')) { + throw new Error('Authentication service error. Please try again later.'); + } + + throw new Error(result.message || 'Google OAuth failed'); } - console.log('Google Token验证成功:', { - email: verifyResult.data?.email, - name: verifyResult.data?.name - }); - - // 第二步:调用Python注册接口进行用户创建和积分发放 - const smartvideoBaseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://77.smartvideo.py.qikongjian.com'; - console.log('🔧 调用 Python 注册接口:', smartvideoBaseUrl); - - const registerResponse = await fetch(`${smartvideoBaseUrl}/api/user_fission/register_with_invite`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, - body: JSON.stringify({ - email: verifyResult.data.email, - name: verifyResult.data.name, - auth_type: 'GOOGLE', - google_user_info: { - email: verifyResult.data.email, - name: verifyResult.data.name, - picture: verifyResult.data.picture || '', - googleId: verifyResult.data.googleId || verifyResult.data.id || '', - verified: verifyResult.data.verified || true, - inviteCode: finalInviteCode - }, - invite_code: finalInviteCode - }) - }); - - console.log('Python注册接口响应状态:', registerResponse.status); - const registerResult = await registerResponse.json(); - - if (!registerResponse.ok || !registerResult.successful) { - console.error('Python注册接口处理失败:', registerResult); - throw new Error(registerResult.message || 'User registration failed'); - } - - console.log('Google OAuth注册成功:', { - userId: registerResult.data?.user_id, - email: registerResult.data?.email + console.log('Google OAuth成功:', { + userId: result.data?.user?.userId, + email: result.data?.user?.email, + isNewUser: result.data?.user?.isNewUser }); // 处理成功结果 - console.log('Google登录成功:', registerResult); + console.log('Google登录成功:', result); setStatus("success"); - setMessage("Login successful! Redirecting to dashboard..."); - + setMessage(result.data.message || "Login successful! Redirecting to dashboard..."); + + // 根据接口文档的响应格式保存用户信息 + const { token, user } = result.data; + // 保存用户信息到localStorage const userData = { - userId: registerResult.data.user_id, - userName: registerResult.data.name, - name: registerResult.data.name, - email: registerResult.data.email, - authType: registerResult.data.auth_type || 'GOOGLE', - isNewUser: true, - inviteCode: registerResult.data.invite_code + userId: user.userId, + userName: user.userName, + name: user.name, + email: user.email, + authType: user.authType, + avatar: user.avatar, + isNewUser: user.isNewUser }; localStorage.setItem('currentUser', JSON.stringify(userData)); - if (registerResult.data.token) { - localStorage.setItem('token', registerResult.data.token); + if (token) { + localStorage.setItem('token', token); } // 2秒后跳转到主页 diff --git a/components/ChatInputBox/ChatInputBox.tsx b/components/ChatInputBox/ChatInputBox.tsx index 5fa4d55..763b7c1 100644 --- a/components/ChatInputBox/ChatInputBox.tsx +++ b/components/ChatInputBox/ChatInputBox.tsx @@ -34,7 +34,6 @@ import { StoryTemplateEntity } from "@/app/service/domain/Entities"; import { useImageStoryServiceHook } from "@/app/service/Interaction/ImageStoryService"; import TemplateCard from "./templateCard"; import { AudioRecorder } from "./AudioRecorder"; -import { useTemplateStoryServiceHook } from "@/app/service/Interaction/templateStoryService"; import { useRouter } from "next/navigation"; import { createMovieProjectV1 } from "@/api/video_flow"; import { @@ -51,6 +50,7 @@ import { H5TemplateDrawer } from "./H5TemplateDrawer"; import { PcPhotoStoryModal } from "./PcPhotoStoryModal"; import { H5PhotoStoryDrawer } from "./H5PhotoStoryDrawer"; import { AspectRatioSelector, AspectRatioValue } from "./AspectRatioSelector"; +import { useTemplateStoryServiceHook } from "@/app/service/Interaction/templateStoryService"; const LauguageOptions = [ { value: "english", label: "English", isVip: false, code:'EN' }, @@ -86,7 +86,7 @@ const VideoDurationOptions = [ * @returns {Function} - 防抖后的函数 */ const debounce = (func: Function, wait: number) => { - let timeout: NodeJS.Timeout; + let timeout: ReturnType; return function executedFunction(...args: any[]) { const later = () => { clearTimeout(timeout); @@ -108,6 +108,14 @@ export function ChatInputBox({ noData }: { noData: boolean }) { // 模板故事弹窗状态 const [isTemplateModalOpen, setIsTemplateModalOpen] = useState(false); + // 模板快捷入口:记录初始模板ID与是否自动聚焦 + const [initialTemplateId, setInitialTemplateId] = useState(undefined); + // 复用模板服务:获取模板列表 + const { + templateStoryList, + isLoading: isTemplateLoading, + getTemplateStoryList, + } = useTemplateStoryServiceHook(); // 图片故事弹窗状态 const [isPhotoStoryModalOpen, setIsPhotoStoryModalOpen] = useState(false); @@ -164,19 +172,19 @@ export function ChatInputBox({ noData }: { noData: boolean }) { }, []); const onConfigChange = (key: K, value: ConfigOptions[K]) => { - setConfigOptions((prev) => ({ + setConfigOptions((prev: ConfigOptions) => ({ ...prev, [key]: value, })); if (key === 'videoDuration') { // 当选择 8s 时,强制关闭剧本扩展并禁用开关 if (value === '8s') { - setConfigOptions((prev) => ({ + setConfigOptions((prev: ConfigOptions) => ({ ...prev, expansion_mode: false, })); } else { - setConfigOptions((prev) => ({ + setConfigOptions((prev: ConfigOptions) => ({ ...prev, expansion_mode: true, })); @@ -184,6 +192,12 @@ export function ChatInputBox({ noData }: { noData: boolean }) { } }; + useEffect(() => { + if (!templateStoryList || templateStoryList.length === 0) { + getTemplateStoryList(); + } + }, []); + const handleCreateVideo = async () => { if (isCreating) return; // 如果正在创建中,直接返回 @@ -279,9 +293,9 @@ export function ChatInputBox({ noData }: { noData: boolean }) { {/* 输入框和Action按钮 - 只在展开状态显示 */} {!isExpanded && ( -
+
{/* 第一行:输入框 */} -
+
{/* 文本输入框 - 改为textarea */}