forked from datawhale/whale-town-end
430 lines
12 KiB
TypeScript
430 lines
12 KiB
TypeScript
/**
|
||
* 登录业务服务
|
||
*
|
||
* 功能描述:
|
||
* - 处理登录相关的业务逻辑和流程控制
|
||
* - 整合核心服务,提供完整的业务功能
|
||
* - 处理业务规则、数据格式化和错误处理
|
||
*
|
||
* 职责分离:
|
||
* - 专注于业务流程和规则实现
|
||
* - 调用核心服务完成具体功能
|
||
* - 为控制器层提供业务接口
|
||
*
|
||
* @author moyin
|
||
* @version 1.0.0
|
||
* @since 2025-12-17
|
||
*/
|
||
|
||
import { Injectable, Logger } from '@nestjs/common';
|
||
import { LoginCoreService, LoginRequest, RegisterRequest, GitHubOAuthRequest, PasswordResetRequest, AuthResult } from '../../core/login_core/login_core.service';
|
||
import { Users } from '../../core/db/users/users.entity';
|
||
|
||
/**
|
||
* 登录响应数据接口
|
||
*/
|
||
export interface LoginResponse {
|
||
/** 用户信息 */
|
||
user: {
|
||
id: string;
|
||
username: string;
|
||
nickname: string;
|
||
email?: string;
|
||
phone?: string;
|
||
avatar_url?: string;
|
||
role: number;
|
||
created_at: Date;
|
||
};
|
||
/** 访问令牌(实际应用中应生成JWT) */
|
||
access_token: string;
|
||
/** 刷新令牌 */
|
||
refresh_token?: string;
|
||
/** 是否为新用户 */
|
||
is_new_user?: boolean;
|
||
/** 消息 */
|
||
message: string;
|
||
}
|
||
|
||
/**
|
||
* 通用响应接口
|
||
*/
|
||
export interface ApiResponse<T = any> {
|
||
/** 是否成功 */
|
||
success: boolean;
|
||
/** 响应数据 */
|
||
data?: T;
|
||
/** 消息 */
|
||
message: string;
|
||
/** 错误代码 */
|
||
error_code?: string;
|
||
}
|
||
|
||
@Injectable()
|
||
export class LoginService {
|
||
private readonly logger = new Logger(LoginService.name);
|
||
|
||
constructor(
|
||
private readonly loginCoreService: LoginCoreService,
|
||
) {}
|
||
|
||
/**
|
||
* 用户登录
|
||
*
|
||
* @param loginRequest 登录请求
|
||
* @returns 登录响应
|
||
*/
|
||
async login(loginRequest: LoginRequest): Promise<ApiResponse<LoginResponse>> {
|
||
try {
|
||
this.logger.log(`用户登录尝试: ${loginRequest.identifier}`);
|
||
|
||
// 调用核心服务进行认证
|
||
const authResult = await this.loginCoreService.login(loginRequest);
|
||
|
||
// 生成访问令牌(实际应用中应使用JWT)
|
||
const accessToken = this.generateAccessToken(authResult.user);
|
||
|
||
// 格式化响应数据
|
||
const response: LoginResponse = {
|
||
user: this.formatUserInfo(authResult.user),
|
||
access_token: accessToken,
|
||
is_new_user: authResult.isNewUser,
|
||
message: '登录成功'
|
||
};
|
||
|
||
this.logger.log(`用户登录成功: ${authResult.user.username} (ID: ${authResult.user.id})`);
|
||
|
||
return {
|
||
success: true,
|
||
data: response,
|
||
message: '登录成功'
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`用户登录失败: ${loginRequest.identifier}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '登录失败',
|
||
error_code: 'LOGIN_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 用户注册
|
||
*
|
||
* @param registerRequest 注册请求
|
||
* @returns 注册响应
|
||
*/
|
||
async register(registerRequest: RegisterRequest): Promise<ApiResponse<LoginResponse>> {
|
||
try {
|
||
this.logger.log(`用户注册尝试: ${registerRequest.username}`);
|
||
|
||
// 调用核心服务进行注册
|
||
const authResult = await this.loginCoreService.register(registerRequest);
|
||
|
||
// 生成访问令牌
|
||
const accessToken = this.generateAccessToken(authResult.user);
|
||
|
||
// 格式化响应数据
|
||
const response: LoginResponse = {
|
||
user: this.formatUserInfo(authResult.user),
|
||
access_token: accessToken,
|
||
is_new_user: true,
|
||
message: '注册成功'
|
||
};
|
||
|
||
this.logger.log(`用户注册成功: ${authResult.user.username} (ID: ${authResult.user.id})`);
|
||
|
||
return {
|
||
success: true,
|
||
data: response,
|
||
message: '注册成功'
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`用户注册失败: ${registerRequest.username}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '注册失败',
|
||
error_code: 'REGISTER_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* GitHub OAuth登录
|
||
*
|
||
* @param oauthRequest OAuth请求
|
||
* @returns 登录响应
|
||
*/
|
||
async githubOAuth(oauthRequest: GitHubOAuthRequest): Promise<ApiResponse<LoginResponse>> {
|
||
try {
|
||
this.logger.log(`GitHub OAuth登录尝试: ${oauthRequest.github_id}`);
|
||
|
||
// 调用核心服务进行OAuth认证
|
||
const authResult = await this.loginCoreService.githubOAuth(oauthRequest);
|
||
|
||
// 生成访问令牌
|
||
const accessToken = this.generateAccessToken(authResult.user);
|
||
|
||
// 格式化响应数据
|
||
const response: LoginResponse = {
|
||
user: this.formatUserInfo(authResult.user),
|
||
access_token: accessToken,
|
||
is_new_user: authResult.isNewUser,
|
||
message: authResult.isNewUser ? 'GitHub账户绑定成功' : 'GitHub登录成功'
|
||
};
|
||
|
||
this.logger.log(`GitHub OAuth成功: ${authResult.user.username} (ID: ${authResult.user.id})`);
|
||
|
||
return {
|
||
success: true,
|
||
data: response,
|
||
message: response.message
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`GitHub OAuth失败: ${oauthRequest.github_id}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : 'GitHub登录失败',
|
||
error_code: 'GITHUB_OAUTH_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送密码重置验证码
|
||
*
|
||
* @param identifier 邮箱或手机号
|
||
* @returns 响应结果
|
||
*/
|
||
async sendPasswordResetCode(identifier: string): Promise<ApiResponse<{ verification_code?: string }>> {
|
||
try {
|
||
this.logger.log(`发送密码重置验证码: ${identifier}`);
|
||
|
||
// 调用核心服务发送验证码
|
||
const verificationCode = await this.loginCoreService.sendPasswordResetCode(identifier);
|
||
|
||
this.logger.log(`密码重置验证码已发送: ${identifier}`);
|
||
|
||
// 实际应用中不应返回验证码,这里仅用于演示
|
||
return {
|
||
success: true,
|
||
data: { verification_code: verificationCode },
|
||
message: '验证码已发送,请查收'
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`发送密码重置验证码失败: ${identifier}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '发送验证码失败',
|
||
error_code: 'SEND_CODE_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 重置密码
|
||
*
|
||
* @param resetRequest 重置请求
|
||
* @returns 响应结果
|
||
*/
|
||
async resetPassword(resetRequest: PasswordResetRequest): Promise<ApiResponse> {
|
||
try {
|
||
this.logger.log(`密码重置尝试: ${resetRequest.identifier}`);
|
||
|
||
// 调用核心服务重置密码
|
||
await this.loginCoreService.resetPassword(resetRequest);
|
||
|
||
this.logger.log(`密码重置成功: ${resetRequest.identifier}`);
|
||
|
||
return {
|
||
success: true,
|
||
message: '密码重置成功'
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`密码重置失败: ${resetRequest.identifier}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '密码重置失败',
|
||
error_code: 'RESET_PASSWORD_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 修改密码
|
||
*
|
||
* @param userId 用户ID
|
||
* @param oldPassword 旧密码
|
||
* @param newPassword 新密码
|
||
* @returns 响应结果
|
||
*/
|
||
async changePassword(userId: bigint, oldPassword: string, newPassword: string): Promise<ApiResponse> {
|
||
try {
|
||
this.logger.log(`修改密码尝试: 用户ID ${userId}`);
|
||
|
||
// 调用核心服务修改密码
|
||
await this.loginCoreService.changePassword(userId, oldPassword, newPassword);
|
||
|
||
this.logger.log(`修改密码成功: 用户ID ${userId}`);
|
||
|
||
return {
|
||
success: true,
|
||
message: '密码修改成功'
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`修改密码失败: 用户ID ${userId}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '密码修改失败',
|
||
error_code: 'CHANGE_PASSWORD_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送邮箱验证码
|
||
*
|
||
* @param email 邮箱地址
|
||
* @returns 响应结果
|
||
*/
|
||
async sendEmailVerification(email: string): Promise<ApiResponse<{ verification_code?: string }>> {
|
||
try {
|
||
this.logger.log(`发送邮箱验证码: ${email}`);
|
||
|
||
// 调用核心服务发送验证码
|
||
const verificationCode = await this.loginCoreService.sendEmailVerification(email);
|
||
|
||
this.logger.log(`邮箱验证码已发送: ${email}`);
|
||
|
||
// 实际应用中不应返回验证码,这里仅用于演示
|
||
return {
|
||
success: true,
|
||
data: { verification_code: verificationCode },
|
||
message: '验证码已发送,请查收邮件'
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`发送邮箱验证码失败: ${email}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '发送验证码失败',
|
||
error_code: 'SEND_EMAIL_VERIFICATION_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证邮箱验证码
|
||
*
|
||
* @param email 邮箱地址
|
||
* @param code 验证码
|
||
* @returns 响应结果
|
||
*/
|
||
async verifyEmailCode(email: string, code: string): Promise<ApiResponse> {
|
||
try {
|
||
this.logger.log(`验证邮箱验证码: ${email}`);
|
||
|
||
// 调用核心服务验证验证码
|
||
const isValid = await this.loginCoreService.verifyEmailCode(email, code);
|
||
|
||
if (isValid) {
|
||
this.logger.log(`邮箱验证成功: ${email}`);
|
||
return {
|
||
success: true,
|
||
message: '邮箱验证成功'
|
||
};
|
||
} else {
|
||
return {
|
||
success: false,
|
||
message: '验证码错误',
|
||
error_code: 'INVALID_VERIFICATION_CODE'
|
||
};
|
||
}
|
||
} catch (error) {
|
||
this.logger.error(`邮箱验证失败: ${email}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '邮箱验证失败',
|
||
error_code: 'EMAIL_VERIFICATION_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 重新发送邮箱验证码
|
||
*
|
||
* @param email 邮箱地址
|
||
* @returns 响应结果
|
||
*/
|
||
async resendEmailVerification(email: string): Promise<ApiResponse<{ verification_code?: string }>> {
|
||
try {
|
||
this.logger.log(`重新发送邮箱验证码: ${email}`);
|
||
|
||
// 调用核心服务重新发送验证码
|
||
const verificationCode = await this.loginCoreService.resendEmailVerification(email);
|
||
|
||
this.logger.log(`邮箱验证码已重新发送: ${email}`);
|
||
|
||
// 实际应用中不应返回验证码,这里仅用于演示
|
||
return {
|
||
success: true,
|
||
data: { verification_code: verificationCode },
|
||
message: '验证码已重新发送,请查收邮件'
|
||
};
|
||
} catch (error) {
|
||
this.logger.error(`重新发送邮箱验证码失败: ${email}`, error instanceof Error ? error.stack : String(error));
|
||
|
||
return {
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '重新发送验证码失败',
|
||
error_code: 'RESEND_EMAIL_VERIFICATION_FAILED'
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 格式化用户信息
|
||
*
|
||
* @param user 用户实体
|
||
* @returns 格式化的用户信息
|
||
*/
|
||
private formatUserInfo(user: Users) {
|
||
return {
|
||
id: user.id.toString(), // 将bigint转换为字符串
|
||
username: user.username,
|
||
nickname: user.nickname,
|
||
email: user.email,
|
||
phone: user.phone,
|
||
avatar_url: user.avatar_url,
|
||
role: user.role,
|
||
created_at: user.created_at
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 生成访问令牌
|
||
*
|
||
* @param user 用户信息
|
||
* @returns 访问令牌
|
||
*/
|
||
private generateAccessToken(user: Users): string {
|
||
// 实际应用中应使用JWT库生成真正的JWT令牌
|
||
// 这里仅用于演示,生成一个简单的令牌
|
||
const payload = {
|
||
userId: user.id.toString(),
|
||
username: user.username,
|
||
role: user.role,
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
// 简单的Base64编码(实际应用中应使用JWT)
|
||
return Buffer.from(JSON.stringify(payload)).toString('base64');
|
||
}
|
||
} |