From 91dc0031d96a36377faafdefbdf2cce9a4b8df43 Mon Sep 17 00:00:00 2001 From: qikongjian Date: Mon, 22 Sep 2025 22:09:49 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=B7=E6=AD=8C=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/auth/google/callback/route.ts | 168 +++++++----------- app/users/oauth/callback/page.tsx | 8 +- docs/jiekou.md | 241 ++++++++++++++++++++++++++ 3 files changed, 311 insertions(+), 106 deletions(-) create mode 100644 docs/jiekou.md diff --git a/app/api/auth/google/callback/route.ts b/app/api/auth/google/callback/route.ts index 6ec8ef4..79b72a3 100644 --- a/app/api/auth/google/callback/route.ts +++ b/app/api/auth/google/callback/route.ts @@ -86,132 +86,98 @@ export async function POST(request: NextRequest) { inviteCode }); - // 第一步:使用authorization code向Google换取access token和id_token - const googleClientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com'; - const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET || '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'; + // 直接将authorization code传递给Java后端处理 + console.log('准备调用Java后端验证Google authorization code...'); - if (!googleClientSecret) { - console.error('Google Client Secret未配置'); - return NextResponse.json( - { - success: false, - message: 'Google Client Secret not configured' - }, - { status: 500 } - ); - } - - console.log('开始向Google交换token...'); - - // 创建fetch配置,包含超时和重试机制 - const fetchOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: new URLSearchParams({ - code, - client_id: googleClientId, - client_secret: googleClientSecret, - redirect_uri: getRedirectUri(request), - grant_type: 'authorization_code', - }), - // 增加超时时间到30秒 - signal: AbortSignal.timeout(30000) - }; - - let tokenResponse; - try { - tokenResponse = await fetch('https://oauth2.googleapis.com/token', fetchOptions); - } catch (fetchError: any) { - console.error('Google API连接失败:', fetchError.message); - - // 如果是超时错误,提供更友好的错误信息 - if (fetchError.name === 'TimeoutError' || fetchError.code === 'UND_ERR_CONNECT_TIMEOUT') { - return NextResponse.json( - { - success: false, - message: 'Google authentication service is temporarily unavailable. Please check your network connection and try again.' - }, - { status: 503 } - ); - } - - throw fetchError; - } - - console.log('Google token exchange响应状态:', tokenResponse.status); - - if (!tokenResponse.ok) { - const errorText = await tokenResponse.text(); - console.error('Google token exchange failed:', errorText); - return NextResponse.json( - { - success: false, - message: 'Failed to exchange authorization code for tokens' - }, - { status: 400 } - ); - } - - const tokenData = await tokenResponse.json(); - console.log('Google token exchange成功,获得token'); - const { id_token } = tokenData; - - if (!id_token) { - console.error('No id_token received from Google'); - return NextResponse.json( - { - success: false, - message: 'No id_token received from Google' - }, - { status: 400 } - ); - } - - // 第二步:使用id_token调用Java后端 + // 第二步:调用Java验证接口(只验证不创建用户) const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai'; - console.log('开始调用Java后端:', javaBaseUrl); + console.log('开始调用Java验证接口:', javaBaseUrl); - const backendResponse = await fetch(`${javaBaseUrl}/api/auth/google/login`, { + const verifyResponse = await fetch(`${javaBaseUrl}/api/auth/google/callback`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ - idToken: id_token, // 使用从Google获取的id_token - action: 'auto', // 自动判断登录或注册 - inviteCode: inviteCode || stateData.inviteCode || undefined + code: code, // Google authorization code + state: state, // state参数 + inviteCode: inviteCode || stateData.inviteCode || undefined, + skipUserCreation: true // 🔑 关键:只验证不创建用户 }) }); - console.log('Java后端响应状态:', backendResponse.status); - const backendResult = await backendResponse.json(); + console.log('Java验证接口响应状态:', verifyResponse.status); + const verifyResult = await verifyResponse.json(); - if (!backendResponse.ok || !backendResult.success) { - console.error('Java后端处理Google OAuth失败:', backendResult); + if (!verifyResponse.ok || !verifyResult.success) { + console.error('Java验证接口处理失败:', verifyResult); return NextResponse.json( { success: false, - message: backendResult.message || 'Google authentication failed' + message: verifyResult.message || 'Google token verification failed' }, - { status: backendResponse.status || 500 } + { status: verifyResponse.status || 500 } ); } - console.log('Google OAuth认证成功:', { - userId: backendResult.data?.user?.userId, - email: backendResult.data?.user?.email + 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: verifyResult.data, + invite_code: inviteCode || stateData.inviteCode || undefined + }) + }); + + console.log('Python注册接口响应状态:', registerResponse.status); + const registerResult = await registerResponse.json(); + + if (!registerResponse.ok || !registerResult.successful) { + console.error('Python注册接口处理失败:', registerResult); + return NextResponse.json( + { + success: false, + message: registerResult.message || 'User registration failed' + }, + { status: registerResponse.status || 500 } + ); + } + + console.log('Google OAuth注册成功:', { + userId: registerResult.data?.user_id, + email: registerResult.data?.email + }); + + // 返回成功结果(统一格式) return NextResponse.json({ success: true, data: { - token: backendResult.data.token, - user: backendResult.data.userInfo || backendResult.data.user, - message: 'Google authentication successful' + token: registerResult.data.token, + user: { + userId: registerResult.data.user_id, + userName: registerResult.data.name, + name: registerResult.data.name, + email: registerResult.data.email, + authType: registerResult.data.auth_type, + isNewUser: true + }, + message: 'Google OAuth registration successful with credit rewards' } }); diff --git a/app/users/oauth/callback/page.tsx b/app/users/oauth/callback/page.tsx index 27b9f70..1f0b7a0 100644 --- a/app/users/oauth/callback/page.tsx +++ b/app/users/oauth/callback/page.tsx @@ -78,12 +78,10 @@ export default function OAuthCallback() { console.log('State数据:', stateData); console.log('最终使用的邀请码:', finalInviteCode); - // 调用后端处理授权码 - 直接调用Java后端 - const JAVA_BASE_URL = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai'; + // 调用Next.js API路由处理授权码(新的两步流程:Java验证 + Python注册) + console.log('调用Next.js API路由:', '/api/auth/google/callback'); - console.log('调用Java后端OAuth接口:', `${JAVA_BASE_URL}/api/auth/google/callback`); - - const response = await fetch(`${JAVA_BASE_URL}/api/auth/google/callback`, { + const response = await fetch('/api/auth/google/callback', { method: 'POST', headers: { 'Accept': 'application/json', diff --git a/docs/jiekou.md b/docs/jiekou.md new file mode 100644 index 0000000..977af09 --- /dev/null +++ b/docs/jiekou.md @@ -0,0 +1,241 @@ +# Google OAuth 前端改造指南 + +## 📋 改造目标 +让Google OAuth注册用户也能享受积分发放和邀请奖励功能。 + +## 🔄 新的流程设计 + +### **改造后流程**: +``` +1. 前端接收Google OAuth回调 +2. 前端 → Java验证接口(只验证,不创建用户) +3. 前端 → Python注册接口(创建用户 + 积分发放) +4. 返回完整的用户信息和token +``` + +--- + +## 🛠️ 前端修改步骤 + +### 📍 修改文件 +**文件**: `video-flow/app/api/auth/google/callback/route.ts` + +### 🔧 核心修改 + +#### **第一步:调用Java验证接口** +```typescript +// 调用Java callback进行Google Token验证(不创建用户) +const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai'; + +const verifyResponse = await fetch(`${javaBaseUrl}/api/auth/google/callback`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + body: JSON.stringify({ + code: code, // Google authorization code + state: state, // state参数 + inviteCode: inviteCode, // 邀请码 + skipUserCreation: true // 🔑 关键:只验证不创建用户 + }) +}); + +const verifyResult = await verifyResponse.json(); +``` + +#### **第二步:调用Python注册接口** +```typescript +// 调用Python注册接口进行用户创建和积分发放 +const smartvideoBaseUrl = process.env.NEXT_PUBLIC_SMARTVIDEO_URL || 'https://smartvideo.test.movieflow.ai'; + +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: verifyResult.data, + invite_code: inviteCode || stateData.inviteCode || undefined + }) +}); + +const registerResult = await registerResponse.json(); +``` + +#### **第三步:返回统一格式结果** +```typescript +return NextResponse.json({ + success: true, + data: { + token: registerResult.data.token, + user: { + userId: registerResult.data.user_id, + userName: registerResult.data.name, + name: registerResult.data.name, + email: registerResult.data.email, + authType: registerResult.data.auth_type, + isNewUser: true + }, + message: 'Google OAuth registration successful with credit rewards' + } +}); +``` + +--- + +## 📚 后端接口文档 + +### 🔵 Java验证接口 + +#### **接口地址** +``` +POST /api/auth/google/callback +``` + +#### **请求参数** +```typescript +{ + code: string, // Google authorization code + state: string, // state参数(可包含邀请码信息) + inviteCode?: string, // 邀请码(可选) + skipUserCreation: boolean // true=只验证不创建用户 +} +``` + +#### **成功响应** +```typescript +{ + success: true, + message: "Google Token验证成功", + data: { + email: string, // 用户邮箱 + name: string, // 用户姓名 + picture: string, // 头像URL + googleId: string, // Google用户ID + verified: boolean, // 是否已验证 + inviteCode: string // 邀请码(如果有) + } +} +``` + +#### **错误响应** +```typescript +{ + success: false, + error: "TOKEN_EXCHANGE_FAILED" | "INVALID_ID_TOKEN", + message: string +} +``` + +--- + +### 🔵 Python注册接口 + +#### **接口地址** +``` +POST /api/user_fission/register_with_invite +``` + +#### **请求参数** +```typescript +{ + email: string, // 用户邮箱 + name?: string, // 用户姓名(可选) + auth_type: "GOOGLE", // 认证类型(固定为GOOGLE) + google_user_info: { // Google用户信息(来自Java验证接口) + email: string, + name: string, + picture: string, + googleId: string, + verified: boolean, + inviteCode?: string + }, + invite_code?: string // 邀请码(可选) +} +``` + +#### **成功响应** +```typescript +{ + successful: true, // 注意:是 successful 不是 success + message: "Google OAuth注册成功", + data: { + user_id: string, // 用户ID(UUID格式) + email: string, // 用户邮箱 + name: string, // 用户姓名 + auth_type: "GOOGLE", // 认证类型 + invite_code?: string, // 邀请码 + token?: string // 认证token(如果有) + } +} +``` + +#### **错误响应** +```typescript +{ + successful: false, + message: string, // 错误信息 + detail?: string // 详细错误信息 +} +``` + +--- + +## 🔧 环境变量配置 + +确保以下环境变量已配置: + +```bash +# Java认证服务地址 +NEXT_PUBLIC_JAVA_URL=https://auth.test.movieflow.ai + +# Python积分服务地址 +NEXT_PUBLIC_SMARTVIDEO_URL=https://smartvideo.test.movieflow.ai +``` + +--- + +## ⚠️ 重要注意事项 + +### 🔍 **关键变更** +1. **接口变更**: `/api/auth/google/login` → `/api/auth/google/callback` +2. **参数变更**: + - 移除: `idToken`, `action` + - 新增: `code`, `state`, `skipUserCreation: true` +3. **响应字段**: Python返回 `successful`,Java返回 `success` +4. **用户ID字段**: Python返回 `user_id`,前端映射为 `userId` + +### 🚨 **错误处理** +- Java验证失败 → 停止流程,返回错误 +- Python注册失败 → 返回详细错误信息 +- 网络异常 → 提供用户友好提示 + +### 🧪 **测试检查点** +- [ ] Google OAuth验证是否成功 +- [ ] 用户是否正确创建并获得积分 +- [ ] 邀请码功能是否正常 +- [ ] 错误处理是否完善 +- [ ] 前端页面跳转是否正常 + +--- + +## 🎯 预期效果 + +改造完成后,Google OAuth用户将获得: + +✅ **注册奖励积分** +✅ **邀请码奖励功能** +✅ **邀请人奖励积分** +✅ **统一的用户体验** +✅ **完整的积分记录** + +--- + +*文档版本: v2.0* +*更新时间: 2025-01-22* +*适用范围: video-flow前端项目* \ No newline at end of file