import { Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import * as bcrypt from 'bcryptjs'; import { PrismaService } from '../../prisma/prisma.service'; @Injectable() export class AuthService { constructor( private prisma: PrismaService, private jwtService: JwtService, ) {} async register(data: { phone?: string; email?: string; password: string; nickname?: string }) { const passwordHash = await bcrypt.hash(data.password, 10); const user = await this.prisma.user.create({ data: { phone: data.phone, email: data.email, passwordHash, nickname: data.nickname || data.phone || data.email?.split('@')[0], }, }); return this.generateTokens(user.id); } async login(account: string, password: string) { const user = await this.prisma.user.findFirst({ where: { OR: [{ phone: account }, { email: account }], deletedAt: null, }, }); if (!user || !user.passwordHash) { throw new UnauthorizedException('账号或密码错误'); } const isValid = await bcrypt.compare(password, user.passwordHash); if (!isValid) { throw new UnauthorizedException('账号或密码错误'); } await this.prisma.user.update({ where: { id: user.id }, data: { lastLoginAt: new Date() }, }); return this.generateTokens(user.id); } async refreshAccessToken(accessToken: string) { try { const payload = this.jwtService.verify(accessToken, { ignoreExpiration: true }); const user = await this.prisma.user.findUnique({ where: { id: payload.sub, deletedAt: null }, }); if (!user || user.status !== 'ACTIVE') { throw new UnauthorizedException('用户不可用'); } return this.generateTokens(user.id); } catch { throw new UnauthorizedException('Token 无效'); } } async getProfile(userId: number) { return this.prisma.user.findUnique({ where: { id: userId }, select: { id: true, phone: true, email: true, nickname: true, avatar: true, status: true, memberPlan: true, memberExpire: true, sandboxDaily: true, createdAt: true, }, }); } private generateTokens(userId: number) { const payload = { sub: userId }; return { accessToken: this.jwtService.sign(payload, { expiresIn: '2h' }), refreshToken: this.jwtService.sign(payload, { expiresIn: '7d' }), }; } }