feat: 登录时同步微信头像和昵称
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,12 +8,34 @@ interface LoginResponse {
|
|||||||
|
|
||||||
export async function wxLogin(): Promise<LoginResponse> {
|
export async function wxLogin(): Promise<LoginResponse> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
// Step 1:静默登录,获取 code
|
||||||
uni.login({
|
uni.login({
|
||||||
provider: 'weixin',
|
provider: 'weixin',
|
||||||
success: async (loginRes) => {
|
success: async (loginRes) => {
|
||||||
try {
|
try {
|
||||||
|
// Step 2: 获取用户微信头像和昵称
|
||||||
|
let nickname: string | undefined
|
||||||
|
let avatarUrl: string | undefined
|
||||||
|
await new Promise<void>((res) => {
|
||||||
|
uni.getUserProfile({
|
||||||
|
desc: '用于完善个人资料',
|
||||||
|
success: (profileRes) => {
|
||||||
|
nickname = profileRes.userInfo.nickName
|
||||||
|
avatarUrl = profileRes.userInfo.avatarUrl
|
||||||
|
res()
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
// 用户拒绝授权,仍可继续登录
|
||||||
|
res()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Step 3: 发送登录请求
|
||||||
const result = await post<LoginResponse>('/auth/login', {
|
const result = await post<LoginResponse>('/auth/login', {
|
||||||
code: loginRes.code,
|
code: loginRes.code,
|
||||||
|
nickname,
|
||||||
|
avatarUrl,
|
||||||
})
|
})
|
||||||
uni.setStorageSync('token', result.token)
|
uni.setStorageSync('token', result.token)
|
||||||
resolve(result)
|
resolve(result)
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ export class AuthController {
|
|||||||
@Post('login')
|
@Post('login')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
async login(@Body() loginDto: LoginDto): Promise<{ token: string; user: User }> {
|
async login(@Body() loginDto: LoginDto): Promise<{ token: string; user: User }> {
|
||||||
return this.authService.login(loginDto.code)
|
return this.authService.login(
|
||||||
|
loginDto.code,
|
||||||
|
loginDto.nickname,
|
||||||
|
loginDto.avatarUrl,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('phone')
|
@Post('phone')
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'
|
|||||||
import { PassportModule } from '@nestjs/passport'
|
import { PassportModule } from '@nestjs/passport'
|
||||||
import { JwtModule } from '@nestjs/jwt'
|
import { JwtModule } from '@nestjs/jwt'
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config'
|
import { ConfigModule, ConfigService } from '@nestjs/config'
|
||||||
import { AuthService } from './auth.service'
|
import { AuthService, RANDOM_FN_TOKEN } from './auth.service'
|
||||||
import { AuthController } from './auth.controller'
|
import { AuthController } from './auth.controller'
|
||||||
import { WechatService } from './wechat.service'
|
import { WechatService } from './wechat.service'
|
||||||
import { JwtStrategy } from './jwt.strategy'
|
import { JwtStrategy } from './jwt.strategy'
|
||||||
@@ -22,7 +22,7 @@ import { RolesGuard } from './roles.guard'
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
providers: [AuthService, WechatService, JwtStrategy, JwtAuthGuard, RolesGuard],
|
providers: [AuthService, WechatService, JwtStrategy, JwtAuthGuard, RolesGuard, { provide: RANDOM_FN_TOKEN, useValue: Math.random }],
|
||||||
exports: [JwtStrategy, JwtAuthGuard, RolesGuard, AuthService],
|
exports: [JwtStrategy, JwtAuthGuard, RolesGuard, AuthService],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
|||||||
@@ -57,7 +57,11 @@ export class AuthService {
|
|||||||
@Inject(RANDOM_FN_TOKEN) private readonly randomFn: () => number = Math.random,
|
@Inject(RANDOM_FN_TOKEN) private readonly randomFn: () => number = Math.random,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async login(code: string): Promise<LoginResult> {
|
async login(
|
||||||
|
code: string,
|
||||||
|
nickname?: string,
|
||||||
|
avatarUrl?: string,
|
||||||
|
): Promise<LoginResult> {
|
||||||
const { openid, unionid, sessionKey } =
|
const { openid, unionid, sessionKey } =
|
||||||
await this.wechatService.code2Session(code)
|
await this.wechatService.code2Session(code)
|
||||||
|
|
||||||
@@ -71,10 +75,23 @@ export class AuthService {
|
|||||||
data: {
|
data: {
|
||||||
openid,
|
openid,
|
||||||
...(unionid !== undefined && { unionid }),
|
...(unionid !== undefined && { unionid }),
|
||||||
nickname: generateDefaultNickname(this.randomFn),
|
nickname: nickname || generateDefaultNickname(this.randomFn),
|
||||||
|
...(avatarUrl && { avatarUrl }),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// Update avatar for existing users if new avatar is provided
|
||||||
|
if (existingUser && avatarUrl) {
|
||||||
|
const updated = await this.prisma.user.update({
|
||||||
|
where: { id: existingUser.id },
|
||||||
|
data: { avatarUrl, ...(nickname && { nickname }) },
|
||||||
|
})
|
||||||
|
sessionKeyStore.set(updated.id, sessionKey)
|
||||||
|
const payload: JwtPayload = { sub: updated.id, role: updated.role as UserRole }
|
||||||
|
const token = this.jwtService.sign(payload)
|
||||||
|
return { token, user: updated }
|
||||||
|
}
|
||||||
|
|
||||||
sessionKeyStore.set(user.id, sessionKey)
|
sessionKeyStore.set(user.id, sessionKey)
|
||||||
|
|
||||||
const payload: JwtPayload = { sub: user.id, role: user.role as UserRole }
|
const payload: JwtPayload = { sub: user.id, role: user.role as UserRole }
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import { IsString, IsNotEmpty } from 'class-validator'
|
import { IsString, IsNotEmpty, IsOptional } from 'class-validator'
|
||||||
|
|
||||||
export class LoginDto {
|
export class LoginDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
code!: string
|
code!: string
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
nickname?: string
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
avatarUrl?: string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user