432 lines
15 KiB
JavaScript
432 lines
15 KiB
JavaScript
// 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;
|
||
|
||
console.log('[Mock] 发送验证码:', { credential, type, purpose });
|
||
|
||
// 生成验证码
|
||
const code = generateVerificationCode();
|
||
mockVerificationCodes.set(credential, {
|
||
code,
|
||
expiresAt: Date.now() + 5 * 60 * 1000 // 5分钟后过期
|
||
});
|
||
|
||
console.log(`[Mock] 验证码已生成: ${credential} -> ${code}`);
|
||
|
||
return HttpResponse.json({
|
||
success: true,
|
||
message: `验证码已发送到 ${credential}(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');
|
||
}
|