| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- 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<UserEntity>;
- 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<UserEntity> {
- 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<UserEntity> {
- 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<number | null> {
- 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);
- }
- }
|