// src/mocks/handlers/auth.js import { http, HttpResponse, delay } from 'msw'; import { mockUsers, mockVerificationCodes, generateVerificationCode, mockWechatSessions, generateWechatSessionId, setCurrentUser, getCurrentUser, clearCurrentUser } from '../data/users'; // 模拟网络延迟(毫秒) const NETWORK_DELAY = 500; export const authHandlers = [ // ==================== 手机验证码登录 ==================== // 1. 发送验证码 http.post('/api/auth/send-verification-code', async ({ request }) => { await delay(NETWORK_DELAY); const body = await request.json(); const { credential, type, purpose } = body; // 生成验证码 const code = generateVerificationCode(); mockVerificationCodes.set(credential, { code, expiresAt: Date.now() + 5 * 60 * 1000 // 5分钟后过期 }); // 超醒目的验证码提示 - 方便开发调试 console.log( `%c\n` + `╔════════════════════════════════════════════╗\n` + `║ 验证码: ${code.padEnd(22)}║\n` + `╚════════════════════════════════════════════╝\n`, 'color: #ffffff; background: #16a34a; font-weight: bold; font-size: 16px; padding: 20px; line-height: 1.8;' ); // 额外的高亮提示 console.log( `%c 验证码: ${code} `, 'color: #ffffff; background: #dc2626; font-weight: bold; font-size: 24px; padding: 15px 30px; border-radius: 8px; margin: 10px 0;' ); return HttpResponse.json({ success: true, message: `验证码已发送到 ${credential}(Mock: ${code})`, // 开发环境下返回验证码,方便测试 dev_code: code }); }), // 1.1 发送手机验证码(前端实际调用的接口) http.post('/api/auth/send-sms-code', async ({ request }) => { await delay(NETWORK_DELAY); const body = await request.json(); const { phone } = body; console.log('[Mock] 发送手机验证码请求:', { phone }); // 生成验证码 const code = generateVerificationCode(); mockVerificationCodes.set(phone, { code, expiresAt: Date.now() + 5 * 60 * 1000 // 5分钟后过期 }); // 超醒目的验证码提示 - 方便开发调试 console.log( `%c\n` + `╔════════════════════════════════════════════╗\n` + `║ 📱 手机验证码: ${code.padEnd(19)}║\n` + `║ 📞 手机号: ${phone.padEnd(23)}║\n` + `╚════════════════════════════════════════════╝\n`, 'color: #ffffff; background: #16a34a; font-weight: bold; font-size: 16px; padding: 20px; line-height: 1.8;' ); // 额外的高亮提示 console.log( `%c 📱 验证码: ${code} `, 'color: #ffffff; background: #dc2626; font-weight: bold; font-size: 24px; padding: 15px 30px; border-radius: 8px; margin: 10px 0;' ); return HttpResponse.json({ success: true, message: `验证码已发送到 ${phone}(Mock: ${code})`, // 开发环境下返回验证码,方便测试 dev_code: code }); }), // 1.2 发送邮箱验证码(前端实际调用的接口) http.post('/api/auth/send-email-code', async ({ request }) => { await delay(NETWORK_DELAY); const body = await request.json(); const { email } = body; console.log('[Mock] 发送邮箱验证码请求:', { email }); // 生成验证码 const code = generateVerificationCode(); mockVerificationCodes.set(email, { code, expiresAt: Date.now() + 5 * 60 * 1000 // 5分钟后过期 }); // 超醒目的验证码提示 - 方便开发调试 console.log( `%c\n` + `╔════════════════════════════════════════════╗\n` + `║ 📧 邮箱验证码: ${code.padEnd(19)}║\n` + `║ 📮 邮箱: ${email.padEnd(27)}║\n` + `╚════════════════════════════════════════════╝\n`, 'color: #ffffff; background: #2563eb; font-weight: bold; font-size: 16px; padding: 20px; line-height: 1.8;' ); // 额外的高亮提示 console.log( `%c 📧 验证码: ${code} `, 'color: #ffffff; background: #dc2626; font-weight: bold; font-size: 24px; padding: 15px 30px; border-radius: 8px; margin: 10px 0;' ); return HttpResponse.json({ success: true, message: `验证码已发送到 ${email}(Mock: ${code})`, // 开发环境下返回验证码,方便测试 dev_code: code }); }), // 2. 验证码登录 http.post('/api/auth/login-with-code', async ({ request }) => { await delay(NETWORK_DELAY); const body = await request.json(); const { credential, verification_code, login_type } = body; console.log('[Mock] 验证码登录:', { credential, verification_code, login_type }); // 验证验证码 const storedCode = mockVerificationCodes.get(credential); if (!storedCode) { return HttpResponse.json({ success: false, error: '验证码不存在或已过期' }, { status: 400 }); } if (storedCode.expiresAt < Date.now()) { mockVerificationCodes.delete(credential); return HttpResponse.json({ success: false, error: '验证码已过期' }, { status: 400 }); } if (storedCode.code !== verification_code) { return HttpResponse.json({ success: false, error: '验证码错误' }, { status: 400 }); } // 验证成功,删除验证码 mockVerificationCodes.delete(credential); // 查找或创建用户 let user = mockUsers[credential]; let isNewUser = false; if (!user) { // 新用户 isNewUser = true; const id = Object.keys(mockUsers).length + 1; user = { id, phone: credential, nickname: `用户${id}`, email: null, avatar_url: `https://i.pravatar.cc/150?img=${id}`, has_wechat: false, created_at: new Date().toISOString(), // 默认订阅信息 - 免费用户 subscription_type: 'free', subscription_status: 'active', subscription_end_date: null, is_subscription_active: true, subscription_days_left: 0 }; mockUsers[credential] = user; console.log('[Mock] 创建新用户:', user); } console.log('[Mock] 登录成功:', user); // 设置当前登录用户 setCurrentUser(user); return HttpResponse.json({ success: true, message: isNewUser ? '注册成功' : '登录成功', isNewUser, user, token: `mock_token_${user.id}_${Date.now()}` }); }), // ==================== 微信登录 ==================== // 3. 获取微信 PC 二维码 http.get('/api/auth/wechat/qrcode', async () => { await delay(NETWORK_DELAY); const sessionId = generateWechatSessionId(); // 创建微信 session mockWechatSessions.set(sessionId, { status: 'waiting', // waiting, scanned, confirmed, expired createdAt: Date.now(), user: null }); // 模拟微信授权 URL(实际是微信的 URL) // 使用真实的微信 AppID 和真实的授权回调地址(必须与微信开放平台配置的域名一致) const mockRedirectUri = encodeURIComponent('http://valuefrontier.cn/api/auth/wechat/callback'); const authUrl = `https://open.weixin.qq.com/connect/qrconnect?appid=wxa8d74c47041b5f87&redirect_uri=${mockRedirectUri}&response_type=code&scope=snsapi_login&state=${sessionId}#wechat_redirect`; console.log('[Mock] 生成微信二维码:', { sessionId, authUrl }); // 3秒后自动模拟扫码(方便测试,已缩短延迟) setTimeout(() => { const session = mockWechatSessions.get(sessionId); if (session && session.status === 'waiting') { session.status = 'scanned'; console.log(`[Mock] 模拟用户扫码: ${sessionId}`); // 再过5秒自动确认登录(延长时间让用户看到 scanned 状态) setTimeout(() => { const session2 = mockWechatSessions.get(sessionId); if (session2 && session2.status === 'scanned') { session2.status = 'authorized'; // ✅ 使用 'authorized' 状态,与后端保持一致 session2.user = { id: 999, nickname: '微信用户', wechat_openid: 'mock_openid_' + sessionId, avatar_url: 'https://ui-avatars.com/api/?name=微信用户&size=150&background=4299e1&color=fff', phone: null, email: null, has_wechat: true, created_at: new Date().toISOString(), // 添加默认订阅信息 subscription_type: 'free', subscription_status: 'active', subscription_end_date: null, is_subscription_active: true, subscription_days_left: 0 }; session2.user_info = { user_id: session2.user.id }; // ✅ 添加 user_info 字段 console.log(`[Mock] 模拟用户确认登录: ${sessionId}`, session2.user); } }, 2000); } }, 3000); return HttpResponse.json({ code: 0, message: '成功', data: { auth_url: authUrl, session_id: sessionId } }); }), // 4. 检查微信扫码状态 http.post('/api/auth/wechat/check', async ({ request }) => { await delay(200); // 轮询请求,延迟短一些 const body = await request.json(); const { session_id } = body; const session = mockWechatSessions.get(session_id); if (!session) { return HttpResponse.json({ code: 404, message: 'Session 不存在', data: { status: 'expired' } }); } // 检查是否过期(5分钟) if (Date.now() - session.createdAt > 5 * 60 * 1000) { session.status = 'expired'; mockWechatSessions.delete(session_id); } console.log('[Mock] 检查微信状态:', { session_id, status: session.status }); // ✅ 返回与后端真实 API 一致的扁平化数据结构 return HttpResponse.json({ status: session.status, user_info: session.user_info, expires_in: Math.floor((session.createdAt + 5 * 60 * 1000 - Date.now()) / 1000) }); }), // 5. 微信登录确认 http.post('/api/auth/login/wechat', async ({ request }) => { await delay(NETWORK_DELAY); const body = await request.json(); const { session_id } = body; const session = mockWechatSessions.get(session_id); if (!session || session.status !== 'authorized') { // ✅ 使用 'authorized' 状态,与前端保持一致 return HttpResponse.json({ success: false, error: '微信登录未确认或已过期' }, { status: 400 }); } const user = session.user; // 清理 session mockWechatSessions.delete(session_id); console.log('[Mock] 微信登录成功:', user); // 设置当前登录用户 setCurrentUser(user); return HttpResponse.json({ success: true, message: '微信登录成功', user, token: `mock_wechat_token_${user.id}_${Date.now()}` }); }), // 6. 获取微信 H5 授权 URL http.post('/api/auth/wechat/h5-auth-url', async ({ request }) => { await delay(NETWORK_DELAY); const body = await request.json(); const { redirect_url } = body; const state = generateWechatSessionId(); const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=mock&redirect_uri=${encodeURIComponent(redirect_url)}&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`; console.log('[Mock] 生成微信 H5 授权 URL:', authUrl); return HttpResponse.json({ code: 0, message: '成功', data: { auth_url: authUrl, state } }); }), // ==================== Session 管理 ==================== // 7. 检查 Session(AuthContext 使用的正确端点) http.get('/api/auth/session', async () => { await delay(300); // 获取当前登录用户 const currentUser = getCurrentUser(); console.log('[Mock] 检查 Session:', currentUser); if (currentUser) { return HttpResponse.json({ success: true, isAuthenticated: true, user: currentUser }); } else { return HttpResponse.json({ success: true, isAuthenticated: false, user: null }); } }), // 8. 检查 Session(旧端点,保留兼容) http.get('/api/auth/check-session', async () => { await delay(300); // 获取当前登录用户 const currentUser = getCurrentUser(); console.log('[Mock] 检查 Session (旧端点):', currentUser); if (currentUser) { return HttpResponse.json({ success: true, isAuthenticated: true, user: currentUser }); } else { return HttpResponse.json({ success: true, isAuthenticated: false, user: null }); } }), // 9. 退出登录 http.post('/api/auth/logout', async () => { await delay(NETWORK_DELAY); console.log('[Mock] 退出登录'); // 清除当前登录用户 clearCurrentUser(); return HttpResponse.json({ success: true, message: '退出成功' }); }) ]; // ==================== Mock 调试工具(仅开发环境) ==================== /** * 暴露全局API,方便手动触发微信扫码模拟 * 使用方式: * 1. 浏览器控制台输入:window.mockWechatScan() * 2. 或者在组件中调用:window.mockWechatScan(sessionId) */ if (process.env.NODE_ENV === 'development' || process.env.REACT_APP_ENABLE_MOCK === 'true') { window.mockWechatScan = (sessionId) => { // 如果没有传入sessionId,尝试获取最新的session let targetSessionId = sessionId; if (!targetSessionId) { // 获取最新创建的session const sessions = Array.from(mockWechatSessions.entries()); if (sessions.length === 0) { console.warn('[Mock API] 没有活跃的微信session,请先获取二维码'); return false; } // 按创建时间排序,获取最新的 const latestSession = sessions.sort((a, b) => b[1].createdAt - a[1].createdAt)[0]; targetSessionId = latestSession[0]; } const session = mockWechatSessions.get(targetSessionId); if (!session) { console.error('[Mock API] Session不存在:', targetSessionId); return false; } if (session.status !== 'waiting') { console.warn('[Mock API] Session状态不是waiting,当前状态:', session.status); return false; } // 立即触发扫码 session.status = 'scanned'; console.log(`[Mock API] ✅ 模拟扫码成功: ${targetSessionId}`); // 1秒后自动确认登录 setTimeout(() => { const session2 = mockWechatSessions.get(targetSessionId); if (session2 && session2.status === 'scanned') { session2.status = 'authorized'; // ✅ 使用 'authorized' 状态,与自动扫码流程保持一致 session2.user = { id: 999, nickname: '微信测试用户', wechat_openid: 'mock_openid_' + targetSessionId, avatar_url: 'https://ui-avatars.com/api/?name=微信测试用户&size=150&background=4299e1&color=fff', phone: null, email: null, has_wechat: true, created_at: new Date().toISOString(), subscription_type: 'free', subscription_status: 'active', subscription_end_date: null, is_subscription_active: true, subscription_days_left: 0 }; session2.user_info = { user_id: session2.user.id }; // ✅ 添加 user_info 字段 console.log(`[Mock API] ✅ 模拟确认登录: ${targetSessionId}`, session2.user); } }, 1000); return true; }; // 暴露获取当前sessions的方法(调试用) window.getMockWechatSessions = () => { const sessions = Array.from(mockWechatSessions.entries()).map(([id, session]) => ({ sessionId: id, status: session.status, createdAt: new Date(session.createdAt).toLocaleString(), hasUser: !!session.user })); console.table(sessions); return sessions; }; console.log('%c[Mock API] 微信登录调试工具已加载', 'color: #00D084; font-weight: bold'); console.log('%c使用方法:', 'color: #666'); console.log(' window.mockWechatScan() - 触发最新session的扫码'); console.log(' window.mockWechatScan(sessionId) - 触发指定session的扫码'); console.log(' window.getMockWechatSessions() - 查看所有活跃的sessions'); }