import { DataSource, Repository } from 'typeorm'; import { UserEntity } from '@/server/modules/users/user.entity'; import { FileService } from '@/server/modules/files/file.service'; import { JWTUtil } from '@/server/utils/jwt.util'; import axios from 'axios'; import process from 'node:process' export class MiniAuthService { private userRepository: Repository; private fileService: FileService; constructor(private dataSource: DataSource) { this.userRepository = dataSource.getRepository(UserEntity); this.fileService = new FileService(dataSource); } async miniLogin(code: string): Promise<{ token: string; user: UserEntity; isNewUser: boolean }> { // 1. 通过code获取openid const openidInfo = await this.getOpenIdByCode(code); // 2. 查找或创建用户 let user = await this.userRepository.findOne({ where: { openid: openidInfo.openid } }); let isNewUser = false; if (!user) { // 自动注册新用户 user = await this.createMiniUser(openidInfo); isNewUser = true; } // 3. 生成token const token = this.generateToken(user); return { token, user, isNewUser }; } async updateUserProfile(userId: number, profile: { nickname?: string; avatarUrl?: string }): Promise { const user = await this.userRepository.findOne({ where: { id: userId }, relations: ['avatarFile'] }); if (!user) throw new Error('用户不存在'); if (profile.nickname) user.nickname = profile.nickname; // 处理头像:如果用户没有头像且提供了小程序头像URL,则下载保存 if (profile.avatarUrl && !user.avatarFileId) { try { const avatarFileId = await this.downloadAndSaveAvatar(profile.avatarUrl, userId); if (avatarFileId) { user.avatarFileId = avatarFileId; } } catch (error) { // 头像下载失败不影响主要功能 console.error('头像下载失败:', error); } } return await this.userRepository.save(user); } private async getOpenIdByCode(code: string): Promise<{ openid: string; unionid?: string; session_key: string }> { const appId = process.env.WX_MINI_APP_ID; const appSecret = process.env.WX_MINI_APP_SECRET; if (!appId || !appSecret) { throw new Error('微信小程序配置缺失'); } const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appId}&secret=${appSecret}&js_code=${code}&grant_type=authorization_code`; try { const response = await axios.get(url, { timeout: 10000 }); if (response.data.errcode) { throw new Error(`微信API错误: ${response.data.errmsg}`); } return { openid: response.data.openid, unionid: response.data.unionid, session_key: response.data.session_key }; } catch (error) { if (axios.isAxiosError(error)) { throw new Error('微信服务器连接失败'); } throw error; } } private async createMiniUser(openidInfo: { openid: string; unionid?: string }): Promise { const user = this.userRepository.create({ username: `wx_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`, password: '', // 小程序用户不需要密码 openid: openidInfo.openid, unionid: openidInfo.unionid, nickname: '微信用户', registrationSource: 'miniapp', isDisabled: 0, isDeleted: 0 }); return await this.userRepository.save(user); } private async downloadAndSaveAvatar(avatarUrl: string, userId: number): Promise { try { const result = await this.fileService.downloadAndSaveFromUrl( avatarUrl, { uploadUserId: userId, customPath: `avatars/`, mimeType: 'image/jpeg' }, { timeout: 10000 } ); return result.file.id; } catch (error) { console.error('下载保存头像失败:', error); return null; } } private generateToken(user: UserEntity): string { return JWTUtil.generateToken(user); } }