@@ -10,6 +10,7 @@ import {
|
||||
CheckPartnerPhoneDto,
|
||||
LoginSmsDto,
|
||||
LoginWechatDto,
|
||||
LoginWechatPhoneDto,
|
||||
RefreshTokenDto,
|
||||
SendSmsDto,
|
||||
} from './dto/auth.dto';
|
||||
@@ -74,6 +75,23 @@ export class UserAuthController {
|
||||
return this.authService.loginUserWechat(dto.code, clientApp, platform, guestId);
|
||||
}
|
||||
|
||||
/** 小程序手机号快捷登录(getPhoneNumber) */
|
||||
@Post('auth/login/wechat-phone')
|
||||
@UseGuards(OptionalJwtAuthGuard)
|
||||
wechatPhoneLogin(@Req() req: Request, @Body() dto: LoginWechatPhoneDto) {
|
||||
const guest = (req as Request & { user?: AuthUser }).user;
|
||||
const guestId = guest?.actorType === 'USER' ? guest.actorId : undefined;
|
||||
const clientApp = resolveUserClientApp(req);
|
||||
const platform = dto.platform ?? (clientApp === ClientApp.USER_MINI ? 'mini' : 'h5');
|
||||
return this.authService.loginUserWechatPhone(
|
||||
dto.phoneCode,
|
||||
clientApp,
|
||||
platform,
|
||||
guestId,
|
||||
dto.loginCode,
|
||||
);
|
||||
}
|
||||
|
||||
@Post('auth/wechat/bind-phone')
|
||||
bindPhoneLegacy(@Req() req: Request, @Body() dto: BindWechatPhoneDto) {
|
||||
return this.authService.bindWechatPhone(
|
||||
|
||||
@@ -751,19 +751,13 @@ export class AuthService {
|
||||
return this.issueToken('PARTNER', account.id, clientApp, false, undefined, undefined, this.partnerTokenPayload(account, primary));
|
||||
}
|
||||
|
||||
async loginUser(phone: string, code: string, clientApp: ClientApp, guestId?: bigint) {
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
const existingUser = await this.prisma.user.findUnique({
|
||||
where: { phone: normalizedPhone },
|
||||
select: { id: true },
|
||||
});
|
||||
await this.verifySmsForUser(
|
||||
normalizedPhone,
|
||||
code,
|
||||
SmsScene.USER_LOGIN,
|
||||
clientApp,
|
||||
guestId ?? existingUser?.id,
|
||||
);
|
||||
/** 手机号已验证后建号/登录并签发会话(短信登录与微信手机号快捷登录共用) */
|
||||
private async issueUserSessionByVerifiedPhone(
|
||||
normalizedPhone: string,
|
||||
clientApp: ClientApp,
|
||||
guestId: bigint | undefined,
|
||||
method: 'sms' | 'wechat_phone',
|
||||
) {
|
||||
let user: UserRow | null = await this.prisma.user.findUnique({
|
||||
where: { phone: normalizedPhone },
|
||||
include: { avatar: true },
|
||||
@@ -816,12 +810,12 @@ export class AuthService {
|
||||
if (guestId && guestId !== user.id) {
|
||||
user = await this.mergeUsers(guestId, user.id);
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'sms_login',
|
||||
extraJson: { method: 'sms', accountMerged: true },
|
||||
eventName: method === 'sms' ? 'sms_login' : 'wechat_phone_login',
|
||||
extraJson: { method, accountMerged: true },
|
||||
});
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'login_success',
|
||||
extraJson: { method: 'sms', accountMerged: true },
|
||||
extraJson: { method, accountMerged: true },
|
||||
});
|
||||
return this.buildSessionResponse(user, clientApp, user.deviceKey, { accountMerged: true });
|
||||
} else {
|
||||
@@ -832,17 +826,70 @@ export class AuthService {
|
||||
if (!user) throw new BadRequestException('登录失败');
|
||||
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'sms_login',
|
||||
extraJson: { method: 'sms' },
|
||||
eventName: method === 'sms' ? 'sms_login' : 'wechat_phone_login',
|
||||
extraJson: { method },
|
||||
});
|
||||
this.analyticsService.trackOneSafe(user.id, clientApp, {
|
||||
eventName: 'login_success',
|
||||
extraJson: { method: 'sms' },
|
||||
extraJson: { method },
|
||||
});
|
||||
|
||||
return this.buildSessionResponse(user, clientApp, user.deviceKey);
|
||||
}
|
||||
|
||||
async loginUser(phone: string, code: string, clientApp: ClientApp, guestId?: bigint) {
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
const existingUser = await this.prisma.user.findUnique({
|
||||
where: { phone: normalizedPhone },
|
||||
select: { id: true },
|
||||
});
|
||||
await this.verifySmsForUser(
|
||||
normalizedPhone,
|
||||
code,
|
||||
SmsScene.USER_LOGIN,
|
||||
clientApp,
|
||||
guestId ?? existingUser?.id,
|
||||
);
|
||||
return this.issueUserSessionByVerifiedPhone(normalizedPhone, clientApp, guestId, 'sms');
|
||||
}
|
||||
|
||||
/** 小程序 getPhoneNumber:用微信返回的 phoneCode 登录/注册,可选 loginCode 绑定 openId */
|
||||
async loginUserWechatPhone(
|
||||
phoneCode: string,
|
||||
clientApp: ClientApp,
|
||||
platform: 'h5' | 'mini' = 'mini',
|
||||
guestId?: bigint,
|
||||
loginCode?: string,
|
||||
) {
|
||||
this.assertWechatEnabled();
|
||||
if (platform !== 'mini') {
|
||||
throw new BadRequestException('仅小程序支持手机号快捷登录');
|
||||
}
|
||||
const phone = await this.wechatProvider.getPhoneNumberByCode(phoneCode, platform);
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
const session = await this.issueUserSessionByVerifiedPhone(
|
||||
normalizedPhone,
|
||||
clientApp,
|
||||
guestId,
|
||||
'wechat_phone',
|
||||
);
|
||||
|
||||
if (loginCode?.trim() && session.actorId) {
|
||||
try {
|
||||
await this.bindUserWechat(
|
||||
BigInt(session.actorId),
|
||||
{ code: loginCode.trim() },
|
||||
clientApp,
|
||||
'mini',
|
||||
);
|
||||
} catch {
|
||||
/* 绑定 openId 失败不阻断已成功的手机号登录 */
|
||||
}
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
async bindPhone(actorId: bigint, phone: string, code: string, clientApp: ClientApp) {
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
await this.verifySmsForUser(normalizedPhone, code, SmsScene.BIND_PHONE, clientApp, actorId);
|
||||
|
||||
@@ -55,6 +55,22 @@ export class LoginWechatDto {
|
||||
platform?: 'h5' | 'mini';
|
||||
}
|
||||
|
||||
/** 小程序 getPhoneNumber 返回的 phoneCode,可选附带 wx.login code 绑定 openId */
|
||||
export class LoginWechatPhoneDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
phoneCode: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
loginCode?: string;
|
||||
|
||||
@IsString()
|
||||
@IsIn(['h5', 'mini'])
|
||||
@IsOptional()
|
||||
platform?: 'h5' | 'mini';
|
||||
}
|
||||
|
||||
export class BindWechatPhoneDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
|
||||
Reference in New Issue
Block a user