Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | import jwt from 'jsonwebtoken'; import { v4 as uuidv4 } from 'uuid'; import { config } from '../../config'; import { prisma } from '../../models'; import { JwtPayload } from '../../types'; // 验证码存储(生产环境应使用 Redis) const smsCodes = new Map<string, { code: string; expireAt: number }>(); // 生成验证码 export function generateSmsCode(phone: string): string { const code = Math.random().toString().slice(2, 6); smsCodes.set(phone, { code, expireAt: Date.now() + 5 * 60 * 1000, // 5分钟有效 }); console.log(`📱 验证码已生成: ${phone} -> ${code}`); return code; } // 验证验证码 export function verifySmsCode(phone: string, code: string): boolean { const stored = smsCodes.get(phone); if (!stored) return false; if (Date.now() > stored.expireAt) { smsCodes.delete(phone); return false; } if (stored.code !== code) return false; smsCodes.delete(phone); return true; } // 生成 JWT Token export function generateToken(userId: string, phone?: string): string { const secret = config.jwt.secret; const payload: Omit<JwtPayload, 'iat' | 'exp'> = { userId, phone }; return jwt.sign(payload, secret, { expiresIn: '7d', }); } // 手机号登录/注册 export async function loginWithPhone(phone: string, code?: string): Promise<{ token: string; user: { id: string; phone: string; nickname: string; avatar: string; memberLevel: number; isNewUser: boolean; }; }> { // 免密登录:code 为空或为"123456"时直接登录(线上线下都关闭验证) const skipVerify = !code || code === '123456'; if (!skipVerify) { // 验证验证码 const isValid = verifySmsCode(phone, code || ''); if (!isValid) { throw new Error('验证码错误或已过期'); } } // 查找或创建用户 let user = await prisma.user.findFirst({ where: { phone } }); let isNewUser = false; if (!user) { user = await prisma.user.create({ data: { phone, nickname: `用户${phone.slice(-4)}`, avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${phone}`, memberLevel: 0, dailyUsage: 0, lastUsageDate: '', }, }); isNewUser = true; } const token = generateToken(user.id.toString(), phone); return { token, user: { id: user.id.toString(), phone: user.phone!, nickname: user.nickname, avatar: user.avatar, memberLevel: user.memberLevel, isNewUser, }, }; } // 获取用户信息 export async function getUserInfo(userId: string) { const user = await prisma.user.findUnique({ where: { id: parseInt(userId) } }); if (!user) { throw new Error('用户不存在'); } return { id: user.id.toString(), phone: user.phone, nickname: user.nickname, avatar: user.avatar, memberLevel: user.memberLevel, memberExpireAt: user.memberExpireAt, dailyUsage: user.dailyUsage, }; } |