diff --git a/src/mocks/handlers/auth.js b/src/mocks/handlers/auth.js index e130d8bf..31f37832 100644 --- a/src/mocks/handlers/auth.js +++ b/src/mocks/handlers/auth.js @@ -140,14 +140,14 @@ export const authHandlers = [ console.log('[Mock] 生成微信二维码:', { sessionId, authUrl }); - // 10秒后自动模拟扫码(方便测试) + // 3秒后自动模拟扫码(方便测试,已缩短延迟) setTimeout(() => { const session = mockWechatSessions.get(sessionId); if (session && session.status === 'waiting') { session.status = 'scanned'; console.log(`[Mock] 模拟用户扫码: ${sessionId}`); - // 再过5秒自动确认登录 + // 再过2秒自动确认登录 setTimeout(() => { const session2 = mockWechatSessions.get(sessionId); if (session2 && session2.status === 'scanned') { @@ -160,13 +160,19 @@ export const authHandlers = [ phone: null, email: null, has_wechat: true, - created_at: new Date().toISOString() + created_at: new Date().toISOString(), + // 添加默认订阅信息 + subscription_type: 'free', + subscription_status: 'active', + subscription_end_date: null, + is_subscription_active: true, + subscription_days_left: 0 }; console.log(`[Mock] 模拟用户确认登录: ${sessionId}`, session2.user); } - }, 5000); + }, 2000); } - }, 10000); + }, 3000); return HttpResponse.json({ code: 0, @@ -334,3 +340,90 @@ export const authHandlers = [ }); }) ]; + +// ==================== 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 = 'confirmed'; + session2.user = { + id: 999, + nickname: '微信测试用户', + wechat_openid: 'mock_openid_' + targetSessionId, + avatar_url: 'https://i.pravatar.cc/150?img=99', + 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 + }; + 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'); +} diff --git a/src/mocks/handlers/index.js b/src/mocks/handlers/index.js index 47236960..9b514e39 100644 --- a/src/mocks/handlers/index.js +++ b/src/mocks/handlers/index.js @@ -5,6 +5,7 @@ import { authHandlers } from './auth'; import { accountHandlers } from './account'; import { simulationHandlers } from './simulation'; import { eventHandlers } from './event'; +import { paymentHandlers } from './payment'; // 可以在这里添加更多的 handlers // import { userHandlers } from './user'; @@ -14,5 +15,6 @@ export const handlers = [ ...accountHandlers, ...simulationHandlers, ...eventHandlers, + ...paymentHandlers, // ...userHandlers, ]; diff --git a/src/mocks/handlers/payment.js b/src/mocks/handlers/payment.js new file mode 100644 index 00000000..2d2f6213 --- /dev/null +++ b/src/mocks/handlers/payment.js @@ -0,0 +1,233 @@ +// src/mocks/handlers/payment.js +import { http, HttpResponse, delay } from 'msw'; +import { getCurrentUser } from '../data/users'; + +// 模拟网络延迟(毫秒) +const NETWORK_DELAY = 500; + +// 模拟订单数据存储 +const mockOrders = new Map(); +let orderIdCounter = 1000; + +export const paymentHandlers = [ + // ==================== 支付订单管理 ==================== + + // 1. 创建支付订单 + http.post('/api/payment/create-order', async ({ request }) => { + await delay(NETWORK_DELAY); + + const currentUser = getCurrentUser(); + if (!currentUser) { + return HttpResponse.json({ + success: false, + error: '未登录' + }, { status: 401 }); + } + + const body = await request.json(); + const { plan_name, billing_cycle } = body; + + console.log('[Mock] 创建支付订单:', { plan_name, billing_cycle, user: currentUser.id }); + + if (!plan_name || !billing_cycle) { + return HttpResponse.json({ + success: false, + error: '参数不完整' + }, { status: 400 }); + } + + // 模拟价格 + const prices = { + pro: { monthly: 0.01, yearly: 0.08 }, + max: { monthly: 0.1, yearly: 0.8 } + }; + + const amount = prices[plan_name]?.[billing_cycle] || 0.01; + + // 创建订单 + const orderId = `ORDER_${orderIdCounter++}_${Date.now()}`; + const order = { + id: orderId, + user_id: currentUser.id, + plan_name, + billing_cycle, + amount, + status: 'pending', + qr_code_url: `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=weixin://wxpay/bizpayurl?pr=mock_${orderId}`, + created_at: new Date().toISOString(), + expires_at: new Date(Date.now() + 30 * 60 * 1000).toISOString() // 30分钟后过期 + }; + + mockOrders.set(orderId, order); + + console.log('[Mock] 订单创建成功:', order); + + // 模拟5秒后自动支付成功(方便测试) + setTimeout(() => { + const existingOrder = mockOrders.get(orderId); + if (existingOrder && existingOrder.status === 'pending') { + existingOrder.status = 'paid'; + existingOrder.paid_at = new Date().toISOString(); + console.log(`[Mock] 订单自动支付成功: ${orderId}`); + } + }, 5000); + + return HttpResponse.json({ + success: true, + data: order + }); + }), + + // 2. 查询订单状态 + http.get('/api/payment/order-status/:orderId', async ({ params }) => { + await delay(300); + + const currentUser = getCurrentUser(); + if (!currentUser) { + return HttpResponse.json({ + success: false, + error: '未登录' + }, { status: 401 }); + } + + const { orderId } = params; + const order = mockOrders.get(orderId); + + console.log('[Mock] 查询订单状态:', { orderId, found: !!order }); + + if (!order) { + return HttpResponse.json({ + success: false, + error: '订单不存在' + }, { status: 404 }); + } + + if (order.user_id !== currentUser.id) { + return HttpResponse.json({ + success: false, + error: '无权访问此订单' + }, { status: 403 }); + } + + return HttpResponse.json({ + success: true, + data: order + }); + }), + + // 3. 获取用户订单列表 + http.get('/api/payment/orders', async () => { + await delay(NETWORK_DELAY); + + const currentUser = getCurrentUser(); + if (!currentUser) { + return HttpResponse.json({ + success: false, + error: '未登录' + }, { status: 401 }); + } + + const userOrders = Array.from(mockOrders.values()) + .filter(order => order.user_id === currentUser.id) + .sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); + + console.log('[Mock] 获取用户订单列表:', { count: userOrders.length }); + + return HttpResponse.json({ + success: true, + data: userOrders + }); + }), + + // 4. 取消订单 + http.post('/api/payment/cancel-order', async ({ request }) => { + await delay(NETWORK_DELAY); + + const currentUser = getCurrentUser(); + if (!currentUser) { + return HttpResponse.json({ + success: false, + error: '未登录' + }, { status: 401 }); + } + + const body = await request.json(); + const { order_id } = body; + + const order = mockOrders.get(order_id); + + if (!order) { + return HttpResponse.json({ + success: false, + error: '订单不存在' + }, { status: 404 }); + } + + if (order.user_id !== currentUser.id) { + return HttpResponse.json({ + success: false, + error: '无权操作此订单' + }, { status: 403 }); + } + + if (order.status !== 'pending') { + return HttpResponse.json({ + success: false, + error: '只能取消待支付的订单' + }, { status: 400 }); + } + + order.status = 'cancelled'; + order.cancelled_at = new Date().toISOString(); + + console.log('[Mock] 订单已取消:', order_id); + + return HttpResponse.json({ + success: true, + message: '订单已取消' + }); + }) +]; + +// ==================== Mock 调试工具(仅开发环境) ==================== + +/** + * 暴露全局API,方便手动触发支付成功 + * 使用方式:window.mockPaymentSuccess(orderId) + */ +if (process.env.NODE_ENV === 'development' || process.env.REACT_APP_ENABLE_MOCK === 'true') { + window.mockPaymentSuccess = (orderId) => { + const order = mockOrders.get(orderId); + if (!order) { + console.error('[Mock Payment] 订单不存在:', orderId); + return false; + } + + if (order.status !== 'pending') { + console.warn('[Mock Payment] 订单状态不是待支付:', order.status); + return false; + } + + order.status = 'paid'; + order.paid_at = new Date().toISOString(); + console.log('[Mock Payment] ✅ 支付成功:', orderId); + return true; + }; + + window.getMockOrders = () => { + const orders = Array.from(mockOrders.entries()).map(([id, order]) => ({ + orderId: id, + status: order.status, + amount: order.amount, + plan: `${order.plan_name} - ${order.billing_cycle}`, + createdAt: new Date(order.created_at).toLocaleString() + })); + console.table(orders); + return orders; + }; + + console.log('%c[Mock Payment] 支付调试工具已加载', 'color: #00D084; font-weight: bold'); + console.log('%c使用方法:', 'color: #666'); + console.log(' window.mockPaymentSuccess(orderId) - 手动触发订单支付成功'); + console.log(' window.getMockOrders() - 查看所有模拟订单'); +}