Files
vf_react/src/mocks/handlers/auth.js
2025-11-04 20:17:56 +08:00

523 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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. 检查 SessionAuthContext 使用的正确端点)
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');
}