短信验证调试成功
This commit is contained in:
@@ -15,8 +15,11 @@ import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { RedisService } from '../../common/redis/redis.service';
|
||||
import { SMS_PROVIDER, WECHAT_PROVIDER } from '../../integrations/integrations.constants';
|
||||
import { ISmsProvider } from '../../integrations/sms/sms.interface';
|
||||
import { SmsCodeStore } from '../../integrations/sms/sms-code.store';
|
||||
import type { IWechatProvider } from '../../integrations/wechat/wechat.interface';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { AnalyticsService } from '../analytics/analytics.service';
|
||||
import { UserAddressService } from './user-address.service';
|
||||
|
||||
import type { User } from '@prisma/client';
|
||||
|
||||
@@ -53,10 +56,33 @@ export class AuthService {
|
||||
private readonly redis: RedisService,
|
||||
@Inject(SMS_PROVIDER) private readonly smsProvider: ISmsProvider,
|
||||
@Inject(WECHAT_PROVIDER) private readonly wechatProvider: IWechatProvider,
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
private readonly smsCodeStore: SmsCodeStore,
|
||||
private readonly userAddressService: UserAddressService,
|
||||
) {}
|
||||
|
||||
private assertMobilePhone(phone: string) {
|
||||
const trimmed = phone.trim();
|
||||
if (!/^1[3-9]\d{9}$/.test(trimmed)) {
|
||||
throw new BadRequestException('请输入正确的手机号码');
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
async sendSms(phone: string, scene: string) {
|
||||
await this.smsProvider.send(phone, scene);
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
if (!Object.values(SmsScene).includes(scene as SmsScene)) {
|
||||
throw new BadRequestException('无效的验证码场景');
|
||||
}
|
||||
await this.smsCodeStore.assertSendCooldown(normalizedPhone);
|
||||
try {
|
||||
await this.smsProvider.send(normalizedPhone, scene);
|
||||
await this.smsCodeStore.setSendCooldown(normalizedPhone);
|
||||
} catch (err) {
|
||||
if (err instanceof BadRequestException) throw err;
|
||||
const message = err instanceof Error ? err.message : '短信发送失败';
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
return { sent: true };
|
||||
}
|
||||
|
||||
@@ -111,9 +137,10 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async loginUser(phone: string, code: string, clientApp: ClientApp, guestId?: bigint) {
|
||||
await this.smsProvider.verify(phone, code, SmsScene.USER_LOGIN);
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
await this.smsProvider.verify(normalizedPhone, code, SmsScene.USER_LOGIN);
|
||||
let user: UserRow | null = await this.prisma.user.findUnique({
|
||||
where: { phone },
|
||||
where: { phone: normalizedPhone },
|
||||
include: { avatar: true },
|
||||
});
|
||||
|
||||
@@ -125,9 +152,9 @@ export class AuthService {
|
||||
user = await this.prisma.user.update({
|
||||
where: { id: guestId },
|
||||
data: {
|
||||
phone,
|
||||
phone: normalizedPhone,
|
||||
phoneVerifiedAt: new Date(),
|
||||
nickname: guest.nickname === '访客' ? `用户${phone.slice(-4)}` : guest.nickname,
|
||||
nickname: guest.nickname === '访客' ? `用户${normalizedPhone.slice(-4)}` : guest.nickname,
|
||||
},
|
||||
include: { avatar: true },
|
||||
});
|
||||
@@ -139,10 +166,10 @@ export class AuthService {
|
||||
if (!user) {
|
||||
user = await this.prisma.user.create({
|
||||
data: {
|
||||
phone,
|
||||
phone: normalizedPhone,
|
||||
phoneVerifiedAt: new Date(),
|
||||
userNo: generateUserNo(),
|
||||
nickname: `用户${phone.slice(-4)}`,
|
||||
nickname: `用户${normalizedPhone.slice(-4)}`,
|
||||
cityPreference: {
|
||||
create: {
|
||||
selectedCityCode: '410100',
|
||||
@@ -170,30 +197,40 @@ export class AuthService {
|
||||
|
||||
if (!user) throw new BadRequestException('登录失败');
|
||||
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'sms_login',
|
||||
extraJson: { method: 'sms' },
|
||||
});
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'login_success',
|
||||
extraJson: { method: 'sms' },
|
||||
});
|
||||
|
||||
return this.buildSessionResponse(user, clientApp, user.deviceKey);
|
||||
}
|
||||
|
||||
async bindPhone(actorId: bigint, phone: string, code: string, clientApp: ClientApp) {
|
||||
await this.smsProvider.verify(phone, code, SmsScene.BIND_PHONE);
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
await this.smsProvider.verify(normalizedPhone, code, SmsScene.BIND_PHONE);
|
||||
const guest = await this.assertActiveUser(actorId);
|
||||
|
||||
if (guest.phone && guest.phoneVerifiedAt) {
|
||||
if (guest.phone === phone) {
|
||||
if (guest.phone === normalizedPhone) {
|
||||
return this.buildSessionResponse(guest, clientApp, guest.deviceKey);
|
||||
}
|
||||
throw new BadRequestException('当前账号已绑定其他手机号');
|
||||
}
|
||||
|
||||
const existing = await this.prisma.user.findUnique({ where: { phone } });
|
||||
const existing = await this.prisma.user.findUnique({ where: { phone: normalizedPhone } });
|
||||
let targetUser: UserRow;
|
||||
|
||||
if (!existing) {
|
||||
targetUser = await this.prisma.user.update({
|
||||
where: { id: guest.id },
|
||||
data: {
|
||||
phone,
|
||||
phone: normalizedPhone,
|
||||
phoneVerifiedAt: new Date(),
|
||||
nickname: guest.nickname === '访客' ? `用户${phone.slice(-4)}` : guest.nickname,
|
||||
nickname: guest.nickname === '访客' ? `用户${normalizedPhone.slice(-4)}` : guest.nickname,
|
||||
},
|
||||
include: { avatar: true },
|
||||
});
|
||||
@@ -210,9 +247,10 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async loginStore(phone: string, code: string, clientApp: ClientApp) {
|
||||
await this.smsProvider.verify(phone, code, SmsScene.STORE_LOGIN);
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
await this.smsProvider.verify(normalizedPhone, code, SmsScene.STORE_LOGIN);
|
||||
const account = await this.prisma.storeAccount.findUnique({
|
||||
where: { phone },
|
||||
where: { phone: normalizedPhone },
|
||||
include: { store: true },
|
||||
});
|
||||
if (!account) throw new BadRequestException('门店账号不存在');
|
||||
@@ -230,9 +268,10 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async loginPartner(phone: string, code: string, clientApp: ClientApp) {
|
||||
await this.smsProvider.verify(phone, code, SmsScene.PARTNER_LOGIN);
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
await this.smsProvider.verify(normalizedPhone, code, SmsScene.PARTNER_LOGIN);
|
||||
const account = await this.prisma.partnerAccount.findUnique({
|
||||
where: { phone },
|
||||
where: { phone: normalizedPhone },
|
||||
include: { partner: true },
|
||||
});
|
||||
if (!account) throw new BadRequestException('合伙人账号不存在');
|
||||
@@ -251,8 +290,9 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async loginHq(phone: string, code: string, clientApp: ClientApp) {
|
||||
await this.smsProvider.verify(phone, code, SmsScene.HQ_LOGIN);
|
||||
const account = await this.prisma.hqAccount.findUnique({ where: { phone } });
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
await this.smsProvider.verify(normalizedPhone, code, SmsScene.HQ_LOGIN);
|
||||
const account = await this.prisma.hqAccount.findUnique({ where: { phone: normalizedPhone } });
|
||||
if (!account) throw new BadRequestException('HQ账号不存在');
|
||||
if (account.status !== 'ACTIVE') throw new BadRequestException('账号已停用');
|
||||
await this.prisma.hqAccount.update({
|
||||
@@ -331,6 +371,14 @@ export class AuthService {
|
||||
},
|
||||
include: { avatar: true },
|
||||
});
|
||||
this.analyticsService.trackOneSafe(activeUser.id, clientApp, {
|
||||
eventName: 'wechat_login',
|
||||
extraJson: { platform },
|
||||
});
|
||||
this.analyticsService.trackOneSafe(activeUser.id, clientApp, {
|
||||
eventName: 'login_success',
|
||||
extraJson: { method: 'wechat', platform },
|
||||
});
|
||||
return this.buildSessionResponse(activeUser, clientApp, activeUser.deviceKey);
|
||||
}
|
||||
|
||||
@@ -460,6 +508,14 @@ export class AuthService {
|
||||
if (!targetUserId) throw new BadRequestException('绑定失败');
|
||||
const user = await this.assertActiveUser(targetUserId);
|
||||
await this.redis.del(`wx:session:${wxSessionKey}`);
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'wechat_phone',
|
||||
extraJson: { method: 'bind_phone' },
|
||||
});
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'login_success',
|
||||
extraJson: { method: 'wechat_bind' },
|
||||
});
|
||||
return this.buildSessionResponse(user, clientApp, user.deviceKey);
|
||||
}
|
||||
|
||||
@@ -646,6 +702,7 @@ export class AuthService {
|
||||
});
|
||||
});
|
||||
|
||||
await this.userAddressService.normalizeDefaultAddress(primaryId);
|
||||
return this.assertActiveUser(primaryId);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user